Latest 15 from a total of 15 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Add Liquidity | 14672302 | 1364 days ago | IN | 0 ETH | 0.00859487 | ||||
| Add Liquidity | 14393977 | 1407 days ago | IN | 0 ETH | 0.00607327 | ||||
| Add Liquidity | 14373080 | 1410 days ago | IN | 0 ETH | 0.00609619 | ||||
| Add Liquidity | 14302087 | 1421 days ago | IN | 0 ETH | 0.01710825 | ||||
| Add Liquidity | 14193758 | 1438 days ago | IN | 0 ETH | 0.01885161 | ||||
| Add Liquidity | 14042378 | 1462 days ago | IN | 0 ETH | 0.02052349 | ||||
| Add Liquidity | 13941939 | 1477 days ago | IN | 0 ETH | 0.01659 | ||||
| Add Liquidity | 13940016 | 1477 days ago | IN | 0 ETH | 0.0323773 | ||||
| Add Liquidity | 13940010 | 1477 days ago | IN | 0 ETH | 0.02909375 | ||||
| Add Liquidity | 13939944 | 1477 days ago | IN | 0 ETH | 0.03370516 | ||||
| Add Liquidity | 13938551 | 1478 days ago | IN | 0 ETH | 0.02998408 | ||||
| Add Liquidity | 13936741 | 1478 days ago | IN | 0 ETH | 0.02680911 | ||||
| Add Liquidity | 13851190 | 1491 days ago | IN | 0 ETH | 0.04951125 | ||||
| Transfer Ownersh... | 13806676 | 1498 days ago | IN | 0 ETH | 0.00402066 | ||||
| Initialize Meta ... | 13806673 | 1498 days ago | IN | 0 ETH | 0.12518884 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| - | 13806673 | 1498 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
MetaSwap
Compiler Version
v0.6.12+commit.27d51765
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "../Swap.sol";
import "./MetaSwapUtils.sol";
/**
* @title MetaSwap - A StableSwap implementation in solidity.
* @notice This contract is responsible for custody of closely pegged assets (eg. group of stablecoins)
* and automatic market making system. Users become an LP (Liquidity Provider) by depositing their tokens
* in desired ratios for an exchange of the pool token that represents their share of the pool.
* Users can burn pool tokens and withdraw their share of token(s).
*
* Each time a swap between the pooled tokens happens, a set fee incurs which effectively gets
* distributed to the LPs.
*
* In case of emergencies, admin can pause additional deposits, swaps, or single-asset withdraws - which
* stops the ratio of the tokens in the pool from changing.
* Users can always withdraw their tokens via multi-asset withdraws.
*
* MetaSwap is a modified version of Swap that allows Swap's LP token to be utilized in pooling with other tokens.
* As an example, if there is a Swap pool consisting of [DAI, USDC, USDT], then a MetaSwap pool can be created
* with [sUSD, BaseSwapLPToken] to allow trades between either the LP token or the underlying tokens and sUSD.
* Note that when interacting with MetaSwap, users cannot deposit or withdraw via underlying tokens. In that case,
* `MetaSwapDeposit.sol` can be additionally deployed to allow interacting with unwrapped representations of the tokens.
*
* @dev Most of the logic is stored as a library `MetaSwapUtils` for the sake of reducing contract's
* deployment size.
*/
contract MetaSwap is Swap {
using MetaSwapUtils for SwapUtils.Swap;
MetaSwapUtils.MetaSwap public metaSwapStorage;
uint256 constant MAX_UINT256 = 2**256 - 1;
/*** EVENTS ***/
// events replicated from SwapUtils to make the ABI easier for dumb
// clients
event TokenSwapUnderlying(
address indexed buyer,
uint256 tokensSold,
uint256 tokensBought,
uint128 soldId,
uint128 boughtId
);
/**
* @notice Get the virtual price, to help calculate profit
* @return the virtual price, scaled to the POOL_PRECISION_DECIMALS
*/
function getVirtualPrice()
external
view
virtual
override
returns (uint256)
{
return MetaSwapUtils.getVirtualPrice(swapStorage, metaSwapStorage);
}
/**
* @notice Calculate amount of tokens you receive on swap
* @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. If the token charges
* a fee on transfers, use the amount that gets transferred after the fee.
* @return amount of tokens the user will receive
*/
function calculateSwap(
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx
) external view virtual override returns (uint256) {
return
MetaSwapUtils.calculateSwap(
swapStorage,
metaSwapStorage,
tokenIndexFrom,
tokenIndexTo,
dx
);
}
/**
* @notice Calculate amount of tokens you receive on swap. For this function,
* the token indices are flattened out so that underlying tokens are represented.
* @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. If the token charges
* a fee on transfers, use the amount that gets transferred after the fee.
* @return amount of tokens the user will receive
*/
function calculateSwapUnderlying(
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx
) external view virtual returns (uint256) {
return
MetaSwapUtils.calculateSwapUnderlying(
swapStorage,
metaSwapStorage,
tokenIndexFrom,
tokenIndexTo,
dx
);
}
/**
* @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 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 token amount the user will receive
*/
function calculateTokenAmount(uint256[] calldata amounts, bool deposit)
external
view
virtual
override
returns (uint256)
{
return
MetaSwapUtils.calculateTokenAmount(
swapStorage,
metaSwapStorage,
amounts,
deposit
);
}
/**
* @notice Calculate the amount of underlying token available to withdraw
* when withdrawing via only single token
* @param tokenAmount the amount of LP token to burn
* @param tokenIndex index of which token will be withdrawn
* @return availableTokenAmount calculated amount of underlying token
* available to withdraw
*/
function calculateRemoveLiquidityOneToken(
uint256 tokenAmount,
uint8 tokenIndex
) external view virtual override returns (uint256) {
return
MetaSwapUtils.calculateWithdrawOneToken(
swapStorage,
metaSwapStorage,
tokenAmount,
tokenIndex
);
}
/*** STATE MODIFYING FUNCTIONS ***/
/**
* @notice This overrides Swap's initialize function to prevent initializing
* without the address of the base Swap contract.
*
* @param _pooledTokens an array of ERC20s this pool will accept
* @param decimals the decimals to use for each pooled token,
* eg 8 for WBTC. Cannot be larger than POOL_PRECISION_DECIMALS
* @param lpTokenName the long-form name of the token to be deployed
* @param lpTokenSymbol the short symbol for the token to be deployed
* @param _a the amplification coefficient * n * (n - 1). See the
* StableSwap paper for details
* @param _fee default swap fee to be initialized with
* @param _adminFee default adminFee to be initialized with
*/
function initialize(
IERC20[] memory _pooledTokens,
uint8[] memory decimals,
string memory lpTokenName,
string memory lpTokenSymbol,
uint256 _a,
uint256 _fee,
uint256 _adminFee,
address lpTokenTargetAddress
) public virtual override initializer {
revert("use initializeMetaSwap() instead");
}
/**
* @notice Initializes this MetaSwap contract with the given parameters.
* MetaSwap uses an existing Swap pool to expand the available liquidity.
* _pooledTokens array should contain the base Swap pool's LP token as
* the last element. For example, if there is a Swap pool consisting of
* [DAI, USDC, USDT]. Then a MetaSwap pool can be created with [sUSD, BaseSwapLPToken]
* as _pooledTokens.
*
* This will also deploy the LPToken that represents users'
* LP position. The owner of LPToken will be this contract - which means
* only this contract is allowed to mint new tokens.
*
* @param _pooledTokens an array of ERC20s this pool will accept. The last
* element must be an existing Swap pool's LP token's address.
* @param decimals the decimals to use for each pooled token,
* eg 8 for WBTC. Cannot be larger than POOL_PRECISION_DECIMALS
* @param lpTokenName the long-form name of the token to be deployed
* @param lpTokenSymbol the short symbol for the token to be deployed
* @param _a the amplification coefficient * n * (n - 1). See the
* StableSwap paper for details
* @param _fee default swap fee to be initialized with
* @param _adminFee default adminFee to be initialized with
*/
function initializeMetaSwap(
IERC20[] memory _pooledTokens,
uint8[] memory decimals,
string memory lpTokenName,
string memory lpTokenSymbol,
uint256 _a,
uint256 _fee,
uint256 _adminFee,
address lpTokenTargetAddress,
ISwap baseSwap
) external virtual initializer {
Swap.initialize(
_pooledTokens,
decimals,
lpTokenName,
lpTokenSymbol,
_a,
_fee,
_adminFee,
lpTokenTargetAddress
);
// MetaSwap initializer
metaSwapStorage.baseSwap = baseSwap;
metaSwapStorage.baseVirtualPrice = baseSwap.getVirtualPrice();
metaSwapStorage.baseCacheLastUpdated = block.timestamp;
// Read all tokens that belong to baseSwap
{
uint8 i;
for (; i < 32; i++) {
try baseSwap.getToken(i) returns (IERC20 token) {
metaSwapStorage.baseTokens.push(token);
token.safeApprove(address(baseSwap), MAX_UINT256);
} catch {
break;
}
}
require(i > 1, "baseSwap must pool at least 2 tokens");
}
// Check the last element of _pooledTokens is owned by baseSwap
IERC20 baseLPToken = _pooledTokens[_pooledTokens.length - 1];
require(
LPToken(address(baseLPToken)).owner() == address(baseSwap),
"baseLPToken is not owned by baseSwap"
);
// Pre-approve the baseLPToken to be used by baseSwap
baseLPToken.safeApprove(address(baseSwap), MAX_UINT256);
}
/**
* @notice Swap two tokens using this pool
* @param tokenIndexFrom the token the user wants to swap from
* @param tokenIndexTo the token the user wants to swap to
* @param dx the amount of tokens the user wants to swap from
* @param minDy the min amount the user would like to receive, or revert.
* @param deadline latest timestamp to accept this transaction
*/
function swap(
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx,
uint256 minDy,
uint256 deadline
)
external
virtual
override
nonReentrant
whenNotPaused
deadlineCheck(deadline)
returns (uint256)
{
return
MetaSwapUtils.swap(
swapStorage,
metaSwapStorage,
tokenIndexFrom,
tokenIndexTo,
dx,
minDy
);
}
/**
* @notice Swap two tokens using this pool and the base pool.
* @param tokenIndexFrom the token the user wants to swap from
* @param tokenIndexTo the token the user wants to swap to
* @param dx the amount of tokens the user wants to swap from
* @param minDy the min amount the user would like to receive, or revert.
* @param deadline latest timestamp to accept this transaction
*/
function swapUnderlying(
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx,
uint256 minDy,
uint256 deadline
)
external
virtual
nonReentrant
whenNotPaused
deadlineCheck(deadline)
returns (uint256)
{
return
MetaSwapUtils.swapUnderlying(
swapStorage,
metaSwapStorage,
tokenIndexFrom,
tokenIndexTo,
dx,
minDy
);
}
/**
* @notice Add liquidity to the pool with the given amounts of tokens
* @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
* @param deadline latest timestamp to accept this transaction
* @return amount of LP token user minted and received
*/
function addLiquidity(
uint256[] calldata amounts,
uint256 minToMint,
uint256 deadline
)
external
virtual
override
nonReentrant
whenNotPaused
deadlineCheck(deadline)
returns (uint256)
{
return
MetaSwapUtils.addLiquidity(
swapStorage,
metaSwapStorage,
amounts,
minToMint
);
}
/**
* @notice Remove liquidity from the pool all in one token. Withdraw fee that decays linearly
* over period of 4 weeks since last deposit will apply.
* @param tokenAmount the amount of the token you want to receive
* @param tokenIndex the index of the token you want to receive
* @param minAmount the minimum amount to withdraw, otherwise revert
* @param deadline latest timestamp to accept this transaction
* @return amount of chosen token user received
*/
function removeLiquidityOneToken(
uint256 tokenAmount,
uint8 tokenIndex,
uint256 minAmount,
uint256 deadline
)
external
virtual
override
nonReentrant
whenNotPaused
deadlineCheck(deadline)
returns (uint256)
{
return
MetaSwapUtils.removeLiquidityOneToken(
swapStorage,
metaSwapStorage,
tokenAmount,
tokenIndex,
minAmount
);
}
/**
* @notice Remove liquidity from the pool, weighted differently than the
* pool's current balances. Withdraw fee that decays linearly
* over period of 4 weeks since last deposit will apply.
* @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.
* @param deadline latest timestamp to accept this transaction
* @return amount of LP tokens burned
*/
function removeLiquidityImbalance(
uint256[] calldata amounts,
uint256 maxBurnAmount,
uint256 deadline
)
external
virtual
override
nonReentrant
whenNotPaused
deadlineCheck(deadline)
returns (uint256)
{
return
MetaSwapUtils.removeLiquidityImbalance(
swapStorage,
metaSwapStorage,
amounts,
maxBurnAmount
);
}
}// 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
// 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.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 "./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
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.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;
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 "./ContextUpgradeable.sol";
import "../proxy/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal initializer {
__Context_init_unchained();
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal initializer {
_paused = false;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
require(!paused(), "Pausable: paused");
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
require(paused(), "Pausable: not paused");
_;
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../proxy/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
function __ReentrancyGuard_init() internal initializer {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal initializer {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
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 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;
/**
* @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
* deploying minimal proxy contracts, also known as "clones".
*
* > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
* > a minimal bytecode implementation that delegates all calls to a known, fixed address.
*
* The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
* (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
* deterministic method.
*
* _Available since v3.4._
*/
library Clones {
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `master`.
*
* This function uses the create opcode, which should never revert.
*/
function clone(address master) internal returns (address instance) {
// solhint-disable-next-line no-inline-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, master))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
instance := create(0, ptr, 0x37)
}
require(instance != address(0), "ERC1167: create failed");
}
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `master`.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy
* the clone. Using the same `master` and `salt` multiple time will revert, since
* the clones cannot be deployed twice at the same address.
*/
function cloneDeterministic(address master, bytes32 salt) internal returns (address instance) {
// solhint-disable-next-line no-inline-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, master))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
instance := create2(0, ptr, 0x37, salt)
}
require(instance != address(0), "ERC1167: create2 failed");
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(address master, bytes32 salt, address deployer) internal pure returns (address predicted) {
// solhint-disable-next-line no-inline-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, master))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
mstore(add(ptr, 0x38), shl(0x60, deployer))
mstore(add(ptr, 0x4c), salt)
mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
predicted := keccak256(add(ptr, 0x37), 0x55)
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(address master, bytes32 salt) internal view returns (address predicted) {
return predictDeterministicAddress(master, salt, address(this));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../../utils/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.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 ERC20 is Context, IERC20 {
using SafeMath 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.
*/
constructor (string memory name_, string memory symbol_) public {
_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 { }
}// 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.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.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;
/*
* @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 Context {
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;
}
}// 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 = 14 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 "./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 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);
__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 ensures that Swap.updateUserWithdrawFees are called everytime.
* 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.12;
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
/**
* @title OwnerPausable
* @notice An ownable contract allows the owner to pause and unpause the
* contract without a delay.
* @dev Only methods using the provided modifiers will be paused.
*/
abstract contract OwnerPausableUpgradeable is
OwnableUpgradeable,
PausableUpgradeable
{
function __OwnerPausable_init() internal initializer {
__Context_init_unchained();
__Ownable_init_unchained();
__Pausable_init_unchained();
}
/**
* @notice Pause the contract. Revert if already paused.
*/
function pause() external onlyOwner {
PausableUpgradeable._pause();
}
/**
* @notice Unpause the contract. Revert if already unpaused.
*/
function unpause() external onlyOwner {
PausableUpgradeable._unpause();
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "./OwnerPausableUpgradeable.sol";
import "./SwapUtils.sol";
import "./AmplificationUtils.sol";
/**
* @title Swap - A StableSwap implementation in solidity.
* @notice This contract is responsible for custody of closely pegged assets (eg. group of stablecoins)
* and automatic market making system. Users become an LP (Liquidity Provider) by depositing their tokens
* in desired ratios for an exchange of the pool token that represents their share of the pool.
* Users can burn pool tokens and withdraw their share of token(s).
*
* Each time a swap between the pooled tokens happens, a set fee incurs which effectively gets
* distributed to the LPs.
*
* In case of emergencies, admin can pause additional deposits, swaps, or single-asset withdraws - which
* stops the ratio of the tokens in the pool from changing.
* Users can always withdraw their tokens via multi-asset withdraws.
*
* @dev Most of the logic is stored as a library `SwapUtils` for the sake of reducing contract's
* deployment size.
*/
contract Swap is OwnerPausableUpgradeable, ReentrancyGuardUpgradeable {
using SafeERC20 for IERC20;
using SafeMath for uint256;
using SwapUtils for SwapUtils.Swap;
using AmplificationUtils for SwapUtils.Swap;
// Struct storing data responsible for automatic market maker functionalities. In order to
// access this data, this contract uses SwapUtils library. For more details, see SwapUtils.sol
SwapUtils.Swap public swapStorage;
// Maps token address to an index in the pool. Used to prevent duplicate tokens in the pool.
// getTokenIndex function also relies on this mapping to retrieve token index.
mapping(address => uint8) private tokenIndexes;
/*** EVENTS ***/
// events replicated from SwapUtils to make the ABI easier for dumb
// clients
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);
event NewWithdrawFee(uint256 newWithdrawFee);
event RampA(
uint256 oldA,
uint256 newA,
uint256 initialTime,
uint256 futureTime
);
event StopRampA(uint256 currentA, uint256 time);
/**
* @notice Initializes this Swap contract with the given parameters.
* This will also clone a LPToken contract that represents users'
* LP positions. The owner of LPToken will be this contract - which means
* only this contract is allowed to mint/burn tokens.
*
* @param _pooledTokens an array of ERC20s this pool will accept
* @param decimals the decimals to use for each pooled token,
* eg 8 for WBTC. Cannot be larger than POOL_PRECISION_DECIMALS
* @param lpTokenName the long-form name of the token to be deployed
* @param lpTokenSymbol the short symbol for the token to be deployed
* @param _a the amplification coefficient * n * (n - 1). See the
* StableSwap paper for details
* @param _fee default swap fee to be initialized with
* @param _adminFee default adminFee to be initialized with
* @param lpTokenTargetAddress the address of an existing LPToken contract to use as a target
*/
function initialize(
IERC20[] memory _pooledTokens,
uint8[] memory decimals,
string memory lpTokenName,
string memory lpTokenSymbol,
uint256 _a,
uint256 _fee,
uint256 _adminFee,
address lpTokenTargetAddress
) public virtual initializer {
__OwnerPausable_init();
__ReentrancyGuard_init();
// Check _pooledTokens and precisions parameter
require(_pooledTokens.length > 1, "_pooledTokens.length <= 1");
require(_pooledTokens.length <= 32, "_pooledTokens.length > 32");
require(
_pooledTokens.length == decimals.length,
"_pooledTokens decimals mismatch"
);
uint256[] memory precisionMultipliers = new uint256[](decimals.length);
for (uint8 i = 0; i < _pooledTokens.length; i++) {
if (i > 0) {
// Check if index is already used. Check if 0th element is a duplicate.
require(
tokenIndexes[address(_pooledTokens[i])] == 0 &&
_pooledTokens[0] != _pooledTokens[i],
"Duplicate tokens"
);
}
require(
address(_pooledTokens[i]) != address(0),
"The 0 address isn't an ERC-20"
);
require(
decimals[i] <= SwapUtils.POOL_PRECISION_DECIMALS,
"Token decimals exceeds max"
);
precisionMultipliers[i] =
10 **
uint256(SwapUtils.POOL_PRECISION_DECIMALS).sub(
uint256(decimals[i])
);
tokenIndexes[address(_pooledTokens[i])] = i;
}
// Check _a, _fee, _adminFee, _withdrawFee parameters
require(_a < AmplificationUtils.MAX_A, "_a exceeds maximum");
require(_fee < SwapUtils.MAX_SWAP_FEE, "_fee exceeds maximum");
require(
_adminFee < SwapUtils.MAX_ADMIN_FEE,
"_adminFee exceeds maximum"
);
// Clone and initialize a LPToken contract
LPToken lpToken = LPToken(Clones.clone(lpTokenTargetAddress));
require(
lpToken.initialize(lpTokenName, lpTokenSymbol),
"could not init lpToken clone"
);
// Initialize swapStorage struct
swapStorage.lpToken = lpToken;
swapStorage.pooledTokens = _pooledTokens;
swapStorage.tokenPrecisionMultipliers = precisionMultipliers;
swapStorage.balances = new uint256[](_pooledTokens.length);
swapStorage.initialA = _a.mul(AmplificationUtils.A_PRECISION);
swapStorage.futureA = _a.mul(AmplificationUtils.A_PRECISION);
// swapStorage.initialATime = 0;
// swapStorage.futureATime = 0;
swapStorage.swapFee = _fee;
swapStorage.adminFee = _adminFee;
}
/*** MODIFIERS ***/
/**
* @notice Modifier to check deadline against current timestamp
* @param deadline latest timestamp to accept this transaction
*/
modifier deadlineCheck(uint256 deadline) {
require(block.timestamp <= deadline, "Deadline not met");
_;
}
/*** VIEW FUNCTIONS ***/
/**
* @notice Return A, the amplification coefficient * n * (n - 1)
* @dev See the StableSwap paper for details
* @return A parameter
*/
function getA() external view virtual returns (uint256) {
return swapStorage.getA();
}
/**
* @notice Return A in its raw precision form
* @dev See the StableSwap paper for details
* @return A parameter in its raw precision form
*/
function getAPrecise() external view virtual returns (uint256) {
return swapStorage.getAPrecise();
}
/**
* @notice Return address of the pooled token at given index. Reverts if tokenIndex is out of range.
* @param index the index of the token
* @return address of the token at given index
*/
function getToken(uint8 index) public view virtual returns (IERC20) {
require(index < swapStorage.pooledTokens.length, "Out of range");
return swapStorage.pooledTokens[index];
}
/**
* @notice Return the index of the given token address. Reverts if no matching
* token is found.
* @param tokenAddress address of the token
* @return the index of the given token address
*/
function getTokenIndex(address tokenAddress)
public
view
virtual
returns (uint8)
{
uint8 index = tokenIndexes[tokenAddress];
require(
address(getToken(index)) == tokenAddress,
"Token does not exist"
);
return index;
}
/**
* @notice Return current balance of the pooled token at given index
* @param index the index of the token
* @return current balance of the pooled token at given index with token's native precision
*/
function getTokenBalance(uint8 index)
external
view
virtual
returns (uint256)
{
require(index < swapStorage.pooledTokens.length, "Index out of range");
return swapStorage.balances[index];
}
/**
* @notice Get the virtual price, to help calculate profit
* @return the virtual price, scaled to the POOL_PRECISION_DECIMALS
*/
function getVirtualPrice() external view virtual returns (uint256) {
return swapStorage.getVirtualPrice();
}
/**
* @notice Calculate amount of tokens you receive on swap
* @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. If the token charges
* a fee on transfers, use the amount that gets transferred after the fee.
* @return amount of tokens the user will receive
*/
function calculateSwap(
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx
) external view virtual returns (uint256) {
return swapStorage.calculateSwap(tokenIndexFrom, tokenIndexTo, dx);
}
/**
* @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 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 token amount the user will receive
*/
function calculateTokenAmount(uint256[] calldata amounts, bool deposit)
external
view
virtual
returns (uint256)
{
return swapStorage.calculateTokenAmount(amounts, deposit);
}
/**
* @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 be burned on withdrawal
* @return array of token balances that the user will receive
*/
function calculateRemoveLiquidity(uint256 amount)
external
view
virtual
returns (uint256[] memory)
{
return swapStorage.calculateRemoveLiquidity(amount);
}
/**
* @notice Calculate the amount of underlying token available to withdraw
* when withdrawing via only single token
* @param tokenAmount the amount of LP token to burn
* @param tokenIndex index of which token will be withdrawn
* @return availableTokenAmount calculated amount of underlying token
* available to withdraw
*/
function calculateRemoveLiquidityOneToken(
uint256 tokenAmount,
uint8 tokenIndex
) external view virtual returns (uint256 availableTokenAmount) {
return swapStorage.calculateWithdrawOneToken(tokenAmount, tokenIndex);
}
/**
* @notice This function reads the accumulated amount of admin fees of the token with given index
* @param index Index of the pooled token
* @return admin's token balance in the token's precision
*/
function getAdminBalance(uint256 index)
external
view
virtual
returns (uint256)
{
return swapStorage.getAdminBalance(index);
}
/*** STATE MODIFYING FUNCTIONS ***/
/**
* @notice Swap two tokens using this pool
* @param tokenIndexFrom the token the user wants to swap from
* @param tokenIndexTo the token the user wants to swap to
* @param dx the amount of tokens the user wants to swap from
* @param minDy the min amount the user would like to receive, or revert.
* @param deadline latest timestamp to accept this transaction
*/
function swap(
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx,
uint256 minDy,
uint256 deadline
)
external
virtual
nonReentrant
whenNotPaused
deadlineCheck(deadline)
returns (uint256)
{
return swapStorage.swap(tokenIndexFrom, tokenIndexTo, dx, minDy);
}
/**
* @notice Add liquidity to the pool with the given amounts of tokens
* @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
* @param deadline latest timestamp to accept this transaction
* @return amount of LP token user minted and received
*/
function addLiquidity(
uint256[] calldata amounts,
uint256 minToMint,
uint256 deadline
)
external
virtual
nonReentrant
whenNotPaused
deadlineCheck(deadline)
returns (uint256)
{
return swapStorage.addLiquidity(amounts, minToMint);
}
/**
* @notice Burn LP tokens to remove liquidity from the pool. Withdraw fee that decays linearly
* over period of 4 weeks since last deposit will apply.
* @dev Liquidity can always be removed, even when the pool is paused.
* @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
* @param deadline latest timestamp to accept this transaction
* @return amounts of tokens user received
*/
function removeLiquidity(
uint256 amount,
uint256[] calldata minAmounts,
uint256 deadline
)
external
virtual
nonReentrant
deadlineCheck(deadline)
returns (uint256[] memory)
{
return swapStorage.removeLiquidity(amount, minAmounts);
}
/**
* @notice Remove liquidity from the pool all in one token. Withdraw fee that decays linearly
* over period of 4 weeks since last deposit will apply.
* @param tokenAmount the amount of the token you want to receive
* @param tokenIndex the index of the token you want to receive
* @param minAmount the minimum amount to withdraw, otherwise revert
* @param deadline latest timestamp to accept this transaction
* @return amount of chosen token user received
*/
function removeLiquidityOneToken(
uint256 tokenAmount,
uint8 tokenIndex,
uint256 minAmount,
uint256 deadline
)
external
virtual
nonReentrant
whenNotPaused
deadlineCheck(deadline)
returns (uint256)
{
return
swapStorage.removeLiquidityOneToken(
tokenAmount,
tokenIndex,
minAmount
);
}
/**
* @notice Remove liquidity from the pool, weighted differently than the
* pool's current balances. Withdraw fee that decays linearly
* over period of 4 weeks since last deposit will apply.
* @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.
* @param deadline latest timestamp to accept this transaction
* @return amount of LP tokens burned
*/
function removeLiquidityImbalance(
uint256[] calldata amounts,
uint256 maxBurnAmount,
uint256 deadline
)
external
virtual
nonReentrant
whenNotPaused
deadlineCheck(deadline)
returns (uint256)
{
return swapStorage.removeLiquidityImbalance(amounts, maxBurnAmount);
}
/*** ADMIN FUNCTIONS ***/
/**
* @notice Withdraw all admin fees to the contract owner
*/
function withdrawAdminFees() external onlyOwner {
swapStorage.withdrawAdminFees(owner());
}
/**
* @notice Update the admin fee. Admin fee takes portion of the swap fee.
* @param newAdminFee new admin fee to be applied on future transactions
*/
function setAdminFee(uint256 newAdminFee) external onlyOwner {
swapStorage.setAdminFee(newAdminFee);
}
/**
* @notice Update the swap fee to be applied on swaps
* @param newSwapFee new swap fee to be applied on future transactions
*/
function setSwapFee(uint256 newSwapFee) external onlyOwner {
swapStorage.setSwapFee(newSwapFee);
}
/**
* @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 futureA the new A to ramp towards
* @param futureTime timestamp when the new A should be reached
*/
function rampA(uint256 futureA, uint256 futureTime) external onlyOwner {
swapStorage.rampA(futureA, futureTime);
}
/**
* @notice Stop ramping A immediately. Reverts if ramp A is already stopped.
*/
function stopRampA() external onlyOwner {
swapStorage.stopRampA();
}
}// 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;
}
/**
* @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 (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.12;
interface IAllowlist {
function getPoolAccountLimit(address poolAddress)
external
view
returns (uint256);
function getPoolCap(address poolAddress) external view returns (uint256);
function verifyAddress(address account, bytes32[] calldata merkleProof)
external
returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "./IAllowlist.sol";
interface ISwap {
// pool data view functions
function getA() external view returns (uint256);
function getAllowlist() external view returns (IAllowlist);
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 isGuarded() external view returns (bool);
function swapStorage()
external
view
returns (
uint256,
uint256,
uint256,
uint256,
uint256,
uint256,
address
);
// 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.12;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "../LPToken.sol";
import "../interfaces/ISwap.sol";
import "../MathUtils.sol";
import "../SwapUtils.sol";
/**
* @title MetaSwapUtils library
* @notice A library to be used within MetaSwap.sol. Contains functions responsible for custody and AMM functionalities.
*
* MetaSwap is a modified version of Swap that allows Swap's LP token to be utilized in pooling with other tokens.
* As an example, if there is a Swap pool consisting of [DAI, USDC, USDT]. Then a MetaSwap pool can be created
* with [sUSD, BaseSwapLPToken] to allow trades between either the LP token or the underlying tokens and sUSD.
*
* @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 MetaSwapUtils {
using SafeERC20 for IERC20;
using SafeMath for uint256;
using MathUtils for uint256;
using AmplificationUtils for SwapUtils.Swap;
/*** EVENTS ***/
event TokenSwap(
address indexed buyer,
uint256 tokensSold,
uint256 tokensBought,
uint128 soldId,
uint128 boughtId
);
event TokenSwapUnderlying(
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 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);
event NewWithdrawFee(uint256 newWithdrawFee);
struct MetaSwap {
// Meta-Swap related parameters
ISwap baseSwap;
uint256 baseVirtualPrice;
uint256 baseCacheLastUpdated;
IERC20[] baseTokens;
}
// 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;
uint256 xpi;
}
// Struct storing variables used in calculation in removeLiquidityImbalance function
// to avoid stack too deep error
struct ManageLiquidityInfo {
uint256 d0;
uint256 d1;
uint256 d2;
LPToken lpToken;
uint256 totalSupply;
uint256 preciseA;
uint256 baseVirtualPrice;
uint256[] tokenPrecisionMultipliers;
uint256[] newBalances;
}
struct SwapUnderlyingInfo {
uint256 x;
uint256 dx;
uint256 dy;
uint256[] tokenPrecisionMultipliers;
uint256[] oldBalances;
IERC20[] baseTokens;
IERC20 tokenFrom;
uint8 metaIndexFrom;
IERC20 tokenTo;
uint8 metaIndexTo;
uint256 baseVirtualPrice;
}
struct CalculateSwapUnderlyingInfo {
uint256 baseVirtualPrice;
ISwap baseSwap;
uint8 baseLPTokenIndex;
uint8 baseTokensLength;
uint8 metaIndexTo;
uint256 x;
uint256 dy;
}
// 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;
// Cache expire time for the stored value of base Swap's virtual price
uint256 public constant BASE_CACHE_EXPIRE_TIME = 10 minutes;
uint256 public constant BASE_VIRTUAL_PRICE_PRECISION = 10**18;
/*** VIEW & PURE FUNCTIONS ***/
/**
* @notice Return the stored value of base Swap's virtual price. If
* value was updated past BASE_CACHE_EXPIRE_TIME, then read it directly
* from the base Swap contract.
* @param metaSwapStorage MetaSwap struct to read from
* @return base Swap's virtual price
*/
function _getBaseVirtualPrice(MetaSwap storage metaSwapStorage)
internal
view
returns (uint256)
{
if (
block.timestamp >
metaSwapStorage.baseCacheLastUpdated + BASE_CACHE_EXPIRE_TIME
) {
return metaSwapStorage.baseSwap.getVirtualPrice();
}
return metaSwapStorage.baseVirtualPrice;
}
function _getBaseSwapFee(ISwap baseSwap)
internal
view
returns (uint256 swapFee)
{
(, , , , swapFee, , ) = baseSwap.swapStorage();
}
/**
* @notice Calculate how much the user would receive when withdrawing via single token
* @param self Swap struct to read from
* @param metaSwapStorage MetaSwap struct to read from
* @param tokenAmount the amount to withdraw in the pool's precision
* @param tokenIndex which token will be withdrawn
* @return dy the amount of token user will receive
*/
function calculateWithdrawOneToken(
SwapUtils.Swap storage self,
MetaSwap storage metaSwapStorage,
uint256 tokenAmount,
uint8 tokenIndex
) external view returns (uint256 dy) {
(dy, ) = _calculateWithdrawOneToken(
self,
tokenAmount,
tokenIndex,
_getBaseVirtualPrice(metaSwapStorage),
self.lpToken.totalSupply()
);
}
function _calculateWithdrawOneToken(
SwapUtils.Swap storage self,
uint256 tokenAmount,
uint8 tokenIndex,
uint256 baseVirtualPrice,
uint256 totalSupply
) internal view returns (uint256, uint256) {
uint256 dy;
uint256 dySwapFee;
{
uint256 currentY;
uint256 newY;
// Calculate how much to withdraw
(dy, newY, currentY) = _calculateWithdrawOneTokenDY(
self,
tokenIndex,
tokenAmount,
baseVirtualPrice,
totalSupply
);
// Calculate the associated swap fee
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
* @param baseVirtualPrice the virtual price of the base swap's LP token
* @return the dy excluding swap fee, the new y after withdrawing one token, and current y
*/
function _calculateWithdrawOneTokenDY(
SwapUtils.Swap storage self,
uint8 tokenIndex,
uint256 tokenAmount,
uint256 baseVirtualPrice,
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, baseVirtualPrice);
require(tokenIndex < xp.length, "Token index out of range");
CalculateWithdrawOneTokenDYInfo
memory v = CalculateWithdrawOneTokenDYInfo(
0,
0,
0,
0,
self._getAPrecise(),
0
);
v.d0 = SwapUtils.getD(xp, v.preciseA);
v.d1 = v.d0.sub(tokenAmount.mul(v.d0).div(totalSupply));
require(tokenAmount <= xp[tokenIndex], "Withdraw exceeds available");
v.newY = SwapUtils.getYD(v.preciseA, tokenIndex, xp, v.d1);
uint256[] memory xpReduced = new uint256[](xp.length);
v.feePerToken = SwapUtils._feePerToken(self.swapFee, xp.length);
for (uint256 i = 0; i < xp.length; i++) {
v.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] = v.xpi.sub(
(
(i == tokenIndex)
? v.xpi.mul(v.d1).div(v.d0).sub(v.newY)
: v.xpi.sub(v.xpi.mul(v.d1).div(v.d0))
).mul(v.feePerToken).div(FEE_DENOMINATOR)
);
}
uint256 dy = xpReduced[tokenIndex].sub(
SwapUtils.getYD(v.preciseA, tokenIndex, xpReduced, v.d1)
);
if (tokenIndex == xp.length.sub(1)) {
dy = dy.mul(BASE_VIRTUAL_PRICE_PRECISION).div(baseVirtualPrice);
v.newY = v.newY.mul(BASE_VIRTUAL_PRICE_PRECISION).div(
baseVirtualPrice
);
xp[tokenIndex] = xp[tokenIndex]
.mul(BASE_VIRTUAL_PRICE_PRECISION)
.div(baseVirtualPrice);
}
dy = dy.sub(1).div(self.tokenPrecisionMultipliers[tokenIndex]);
return (dy, v.newY, xp[tokenIndex]);
}
/**
* @notice Given a set of balances and precision multipliers, return the
* precision-adjusted balances. The last element will also get scaled up by
* the given baseVirtualPrice.
*
* @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.
*
* @param baseVirtualPrice the base virtual price to scale the balance of the
* base Swap's LP token.
*
* @return an array of amounts "scaled" to the pool's precision
*/
function _xp(
uint256[] memory balances,
uint256[] memory precisionMultipliers,
uint256 baseVirtualPrice
) internal pure returns (uint256[] memory) {
uint256[] memory xp = SwapUtils._xp(balances, precisionMultipliers);
uint256 baseLPTokenIndex = balances.length.sub(1);
xp[baseLPTokenIndex] = xp[baseLPTokenIndex].mul(baseVirtualPrice).div(
BASE_VIRTUAL_PRICE_PRECISION
);
return xp;
}
/**
* @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(SwapUtils.Swap storage self, uint256 baseVirtualPrice)
internal
view
returns (uint256[] memory)
{
return
_xp(
self.balances,
self.tokenPrecisionMultipliers,
baseVirtualPrice
);
}
/**
* @notice Get the virtual price, to help calculate profit
* @param self Swap struct to read from
* @param metaSwapStorage MetaSwap struct to read from
* @return the virtual price, scaled to precision of BASE_VIRTUAL_PRICE_PRECISION
*/
function getVirtualPrice(
SwapUtils.Swap storage self,
MetaSwap storage metaSwapStorage
) external view returns (uint256) {
uint256 d = SwapUtils.getD(
_xp(
self.balances,
self.tokenPrecisionMultipliers,
_getBaseVirtualPrice(metaSwapStorage)
),
self._getAPrecise()
);
uint256 supply = self.lpToken.totalSupply();
if (supply != 0) {
return d.mul(BASE_VIRTUAL_PRICE_PRECISION).div(supply);
}
return 0;
}
/**
* @notice Externally calculates a swap between two tokens. The SwapUtils.Swap storage and
* MetaSwap storage should be from the same MetaSwap contract.
* @param self Swap struct to read from
* @param metaSwapStorage MetaSwap struct from the same contract
* @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(
SwapUtils.Swap storage self,
MetaSwap storage metaSwapStorage,
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx
) external view returns (uint256 dy) {
(dy, ) = _calculateSwap(
self,
tokenIndexFrom,
tokenIndexTo,
dx,
_getBaseVirtualPrice(metaSwapStorage)
);
}
/**
* @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.
* @param baseVirtualPrice the virtual price of the base LP token
* @return dy the number of tokens the user will get and dyFee the associated fee
*/
function _calculateSwap(
SwapUtils.Swap storage self,
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx,
uint256 baseVirtualPrice
) internal view returns (uint256 dy, uint256 dyFee) {
uint256[] memory xp = _xp(self, baseVirtualPrice);
require(
tokenIndexFrom < xp.length && tokenIndexTo < xp.length,
"Token index out of range"
);
uint256 baseLPTokenIndex = xp.length.sub(1);
uint256 x = dx.mul(self.tokenPrecisionMultipliers[tokenIndexFrom]);
if (tokenIndexFrom == baseLPTokenIndex) {
// When swapping from a base Swap token, scale up dx by its virtual price
x = x.mul(baseVirtualPrice).div(BASE_VIRTUAL_PRICE_PRECISION);
}
x = x.add(xp[tokenIndexFrom]);
uint256 y = SwapUtils.getY(
self._getAPrecise(),
tokenIndexFrom,
tokenIndexTo,
x,
xp
);
dy = xp[tokenIndexTo].sub(y).sub(1);
if (tokenIndexTo == baseLPTokenIndex) {
// When swapping to a base Swap token, scale down dy by its virtual price
dy = dy.mul(BASE_VIRTUAL_PRICE_PRECISION).div(baseVirtualPrice);
}
dyFee = dy.mul(self.swapFee).div(FEE_DENOMINATOR);
dy = dy.sub(dyFee);
dy = dy.div(self.tokenPrecisionMultipliers[tokenIndexTo]);
}
/**
* @notice Calculates the expected return amount from swapping between
* the pooled tokens and the underlying tokens of the base Swap pool.
*
* @param self Swap struct to read from
* @param metaSwapStorage MetaSwap struct from the same contract
* @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 calculateSwapUnderlying(
SwapUtils.Swap storage self,
MetaSwap storage metaSwapStorage,
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx
) external view returns (uint256) {
CalculateSwapUnderlyingInfo memory v = CalculateSwapUnderlyingInfo(
_getBaseVirtualPrice(metaSwapStorage),
metaSwapStorage.baseSwap,
0,
uint8(metaSwapStorage.baseTokens.length),
0,
0,
0
);
uint256[] memory xp = _xp(self, v.baseVirtualPrice);
v.baseLPTokenIndex = uint8(xp.length.sub(1));
{
uint8 maxRange = v.baseLPTokenIndex + v.baseTokensLength;
require(
tokenIndexFrom < maxRange && tokenIndexTo < maxRange,
"Token index out of range"
);
}
if (tokenIndexFrom < v.baseLPTokenIndex) {
// tokenFrom is from this pool
v.x = xp[tokenIndexFrom].add(
dx.mul(self.tokenPrecisionMultipliers[tokenIndexFrom])
);
} else {
// tokenFrom is from the base pool
tokenIndexFrom = tokenIndexFrom - v.baseLPTokenIndex;
if (tokenIndexTo < v.baseLPTokenIndex) {
uint256[] memory baseInputs = new uint256[](v.baseTokensLength);
baseInputs[tokenIndexFrom] = dx;
v.x = v
.baseSwap
.calculateTokenAmount(baseInputs, true)
.mul(v.baseVirtualPrice)
.div(BASE_VIRTUAL_PRICE_PRECISION);
// when adding to the base pool,you pay approx 50% of the swap fee
v.x = v
.x
.sub(
v.x.mul(_getBaseSwapFee(metaSwapStorage.baseSwap)).div(
FEE_DENOMINATOR.mul(2)
)
)
.add(xp[v.baseLPTokenIndex]);
} else {
// both from and to are from the base pool
return
v.baseSwap.calculateSwap(
tokenIndexFrom,
tokenIndexTo - v.baseLPTokenIndex,
dx
);
}
tokenIndexFrom = v.baseLPTokenIndex;
}
v.metaIndexTo = v.baseLPTokenIndex;
if (tokenIndexTo < v.baseLPTokenIndex) {
v.metaIndexTo = tokenIndexTo;
}
{
uint256 y = SwapUtils.getY(
self._getAPrecise(),
tokenIndexFrom,
v.metaIndexTo,
v.x,
xp
);
v.dy = xp[v.metaIndexTo].sub(y).sub(1);
uint256 dyFee = v.dy.mul(self.swapFee).div(FEE_DENOMINATOR);
v.dy = v.dy.sub(dyFee);
}
if (tokenIndexTo < v.baseLPTokenIndex) {
// tokenTo is from this pool
v.dy = v.dy.div(self.tokenPrecisionMultipliers[v.metaIndexTo]);
} else {
// tokenTo is from the base pool
v.dy = v.baseSwap.calculateRemoveLiquidityOneToken(
v.dy.mul(BASE_VIRTUAL_PRICE_PRECISION).div(v.baseVirtualPrice),
tokenIndexTo - v.baseLPTokenIndex
);
}
return v.dy;
}
/**
* @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 metaSwapStorage MetaSwap 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(
SwapUtils.Swap storage self,
MetaSwap storage metaSwapStorage,
uint256[] calldata amounts,
bool deposit
) external view returns (uint256) {
uint256 a = self._getAPrecise();
uint256 d0;
uint256 d1;
{
uint256 baseVirtualPrice = _getBaseVirtualPrice(metaSwapStorage);
uint256[] memory balances1 = self.balances;
uint256[] memory tokenPrecisionMultipliers = self
.tokenPrecisionMultipliers;
uint256 numTokens = balances1.length;
d0 = SwapUtils.getD(
_xp(balances1, tokenPrecisionMultipliers, baseVirtualPrice),
a
);
for (uint256 i = 0; i < numTokens; i++) {
if (deposit) {
balances1[i] = balances1[i].add(amounts[i]);
} else {
balances1[i] = balances1[i].sub(
amounts[i],
"Cannot withdraw more than available"
);
}
}
d1 = SwapUtils.getD(
_xp(balances1, tokenPrecisionMultipliers, baseVirtualPrice),
a
);
}
uint256 totalSupply = self.lpToken.totalSupply();
if (deposit) {
return d1.sub(d0).mul(totalSupply).div(d0);
} else {
return d0.sub(d1).mul(totalSupply).div(d0);
}
}
/*** STATE MODIFYING FUNCTIONS ***/
/**
* @notice swap two tokens in the pool
* @param self Swap struct to read from and write to
* @param metaSwapStorage MetaSwap 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(
SwapUtils.Swap storage self,
MetaSwap storage metaSwapStorage,
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx,
uint256 minDy
) external returns (uint256) {
{
uint256 pooledTokensLength = self.pooledTokens.length;
require(
tokenIndexFrom < pooledTokensLength &&
tokenIndexTo < pooledTokensLength,
"Token index is out of range"
);
}
uint256 transferredDx;
{
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
transferredDx = tokenFrom.balanceOf(address(this)).sub(
beforeBalance
);
}
}
(uint256 dy, uint256 dyFee) = _calculateSwap(
self,
tokenIndexFrom,
tokenIndexTo,
transferredDx,
_updateBaseVirtualPrice(metaSwapStorage)
);
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] = self.balances[tokenIndexFrom].add(
transferredDx
);
self.balances[tokenIndexTo] = self.balances[tokenIndexTo].sub(dy).sub(
dyAdminFee
);
self.pooledTokens[tokenIndexTo].safeTransfer(msg.sender, dy);
emit TokenSwap(
msg.sender,
transferredDx,
dy,
tokenIndexFrom,
tokenIndexTo
);
return dy;
}
/**
* @notice Swaps with the underlying tokens of the base Swap pool. For this function,
* the token indices are flattened out so that underlying tokens are represented
* in the indices.
* @dev Since this calls multiple external functions during the execution,
* it is recommended to protect any function that depends on this with reentrancy guards.
* @param self Swap struct to read from and write to
* @param metaSwapStorage MetaSwap 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 swapUnderlying(
SwapUtils.Swap storage self,
MetaSwap storage metaSwapStorage,
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx,
uint256 minDy
) external returns (uint256) {
SwapUnderlyingInfo memory v = SwapUnderlyingInfo(
0,
0,
0,
self.tokenPrecisionMultipliers,
self.balances,
metaSwapStorage.baseTokens,
IERC20(address(0)),
0,
IERC20(address(0)),
0,
_updateBaseVirtualPrice(metaSwapStorage)
);
uint8 baseLPTokenIndex = uint8(v.oldBalances.length.sub(1));
{
uint8 maxRange = uint8(baseLPTokenIndex + v.baseTokens.length);
require(
tokenIndexFrom < maxRange && tokenIndexTo < maxRange,
"Token index out of range"
);
}
ISwap baseSwap = metaSwapStorage.baseSwap;
// Find the address of the token swapping from and the index in MetaSwap's token list
if (tokenIndexFrom < baseLPTokenIndex) {
v.tokenFrom = self.pooledTokens[tokenIndexFrom];
v.metaIndexFrom = tokenIndexFrom;
} else {
v.tokenFrom = v.baseTokens[tokenIndexFrom - baseLPTokenIndex];
v.metaIndexFrom = baseLPTokenIndex;
}
// Find the address of the token swapping to and the index in MetaSwap's token list
if (tokenIndexTo < baseLPTokenIndex) {
v.tokenTo = self.pooledTokens[tokenIndexTo];
v.metaIndexTo = tokenIndexTo;
} else {
v.tokenTo = v.baseTokens[tokenIndexTo - baseLPTokenIndex];
v.metaIndexTo = baseLPTokenIndex;
}
// Check for possible fee on transfer
v.dx = v.tokenFrom.balanceOf(address(this));
v.tokenFrom.safeTransferFrom(msg.sender, address(this), dx);
v.dx = v.tokenFrom.balanceOf(address(this)).sub(v.dx); // update dx in case of fee on transfer
if (
tokenIndexFrom < baseLPTokenIndex || tokenIndexTo < baseLPTokenIndex
) {
// Either one of the tokens belongs to the MetaSwap tokens list
uint256[] memory xp = _xp(
v.oldBalances,
v.tokenPrecisionMultipliers,
v.baseVirtualPrice
);
if (tokenIndexFrom < baseLPTokenIndex) {
// Swapping from a MetaSwap token
v.x = xp[tokenIndexFrom].add(
dx.mul(v.tokenPrecisionMultipliers[tokenIndexFrom])
);
} else {
// Swapping from one of the tokens hosted in the base Swap
// This case requires adding the underlying token to the base Swap, then
// using the base LP token to swap to the desired token
uint256[] memory baseAmounts = new uint256[](
v.baseTokens.length
);
baseAmounts[tokenIndexFrom - baseLPTokenIndex] = v.dx;
// Add liquidity to the base Swap contract and receive base LP token
v.dx = baseSwap.addLiquidity(baseAmounts, 0, block.timestamp);
// Calculate the value of total amount of baseLPToken we end up with
v.x = v
.dx
.mul(v.baseVirtualPrice)
.div(BASE_VIRTUAL_PRICE_PRECISION)
.add(xp[baseLPTokenIndex]);
}
// Calculate how much to withdraw in MetaSwap level and the the associated swap fee
uint256 dyFee;
{
uint256 y = SwapUtils.getY(
self._getAPrecise(),
v.metaIndexFrom,
v.metaIndexTo,
v.x,
xp
);
v.dy = xp[v.metaIndexTo].sub(y).sub(1);
if (tokenIndexTo >= baseLPTokenIndex) {
// When swapping to a base Swap token, scale down dy by its virtual price
v.dy = v.dy.mul(BASE_VIRTUAL_PRICE_PRECISION).div(
v.baseVirtualPrice
);
}
dyFee = v.dy.mul(self.swapFee).div(FEE_DENOMINATOR);
v.dy = v.dy.sub(dyFee).div(
v.tokenPrecisionMultipliers[v.metaIndexTo]
);
}
// Update the balances array according to the calculated input and output amount
{
uint256 dyAdminFee = dyFee.mul(self.adminFee).div(
FEE_DENOMINATOR
);
dyAdminFee = dyAdminFee.div(
v.tokenPrecisionMultipliers[v.metaIndexTo]
);
self.balances[v.metaIndexFrom] = v
.oldBalances[v.metaIndexFrom]
.add(v.dx);
self.balances[v.metaIndexTo] = v
.oldBalances[v.metaIndexTo]
.sub(v.dy)
.sub(dyAdminFee);
}
if (tokenIndexTo >= baseLPTokenIndex) {
// When swapping to a token that belongs to the base Swap, burn the LP token
// and withdraw the desired token from the base pool
uint256 oldBalance = v.tokenTo.balanceOf(address(this));
baseSwap.removeLiquidityOneToken(
v.dy,
tokenIndexTo - baseLPTokenIndex,
0,
block.timestamp
);
v.dy = v.tokenTo.balanceOf(address(this)) - oldBalance;
}
// Check the amount of token to send meets minDy
require(v.dy >= minDy, "Swap didn't result in min tokens");
} else {
// Both tokens are from the base Swap pool
// Do a swap through the base Swap
v.dy = v.tokenTo.balanceOf(address(this));
baseSwap.swap(
tokenIndexFrom - baseLPTokenIndex,
tokenIndexTo - baseLPTokenIndex,
v.dx,
minDy,
block.timestamp
);
v.dy = v.tokenTo.balanceOf(address(this)).sub(v.dy);
}
// Send the desired token to the caller
v.tokenTo.safeTransfer(msg.sender, v.dy);
emit TokenSwapUnderlying(
msg.sender,
dx,
v.dy,
tokenIndexFrom,
tokenIndexTo
);
return v.dy;
}
/**
* @notice Add liquidity to the pool
* @param self Swap struct to read from and write to
* @param metaSwapStorage MetaSwap 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(
SwapUtils.Swap storage self,
MetaSwap storage metaSwapStorage,
uint256[] memory amounts,
uint256 minToMint
) external returns (uint256) {
IERC20[] memory pooledTokens = self.pooledTokens;
require(
amounts.length == pooledTokens.length,
"Amounts must match pooled tokens"
);
uint256[] memory fees = new uint256[](pooledTokens.length);
// current state
ManageLiquidityInfo memory v = ManageLiquidityInfo(
0,
0,
0,
self.lpToken,
0,
self._getAPrecise(),
_updateBaseVirtualPrice(metaSwapStorage),
self.tokenPrecisionMultipliers,
self.balances
);
v.totalSupply = v.lpToken.totalSupply();
if (v.totalSupply != 0) {
v.d0 = SwapUtils.getD(
_xp(
v.newBalances,
v.tokenPrecisionMultipliers,
v.baseVirtualPrice
),
v.preciseA
);
}
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
);
}
v.newBalances[i] = v.newBalances[i].add(amounts[i]);
}
// invariant after change
v.d1 = SwapUtils.getD(
_xp(v.newBalances, v.tokenPrecisionMultipliers, v.baseVirtualPrice),
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 toMint;
if (v.totalSupply != 0) {
uint256 feePerToken = SwapUtils._feePerToken(
self.swapFee,
pooledTokens.length
);
for (uint256 i = 0; i < pooledTokens.length; i++) {
uint256 idealBalance = v.d1.mul(self.balances[i]).div(v.d0);
fees[i] = feePerToken
.mul(idealBalance.difference(v.newBalances[i]))
.div(FEE_DENOMINATOR);
self.balances[i] = v.newBalances[i].sub(
fees[i].mul(self.adminFee).div(FEE_DENOMINATOR)
);
v.newBalances[i] = v.newBalances[i].sub(fees[i]);
}
v.d2 = SwapUtils.getD(
_xp(
v.newBalances,
v.tokenPrecisionMultipliers,
v.baseVirtualPrice
),
v.preciseA
);
toMint = v.d2.sub(v.d0).mul(v.totalSupply).div(v.d0);
} else {
// the initial depositor doesn't pay fees
self.balances = v.newBalances;
toMint = v.d1;
}
require(toMint >= minToMint, "Couldn't mint min requested");
// mint the user's LP tokens
self.lpToken.mint(msg.sender, toMint);
emit AddLiquidity(
msg.sender,
amounts,
fees,
v.d1,
v.totalSupply.add(toMint)
);
return toMint;
}
/**
* @notice Remove liquidity from the pool all in one token.
* @param self Swap struct to read from and write to
* @param metaSwapStorage MetaSwap 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(
SwapUtils.Swap storage self,
MetaSwap storage metaSwapStorage,
uint256 tokenAmount,
uint8 tokenIndex,
uint256 minAmount
) external returns (uint256) {
LPToken lpToken = self.lpToken;
uint256 totalSupply = lpToken.totalSupply();
uint256 numTokens = self.pooledTokens.length;
require(tokenAmount <= lpToken.balanceOf(msg.sender), ">LP.balanceOf");
require(tokenIndex < numTokens, "Token not found");
uint256 dyFee;
uint256 dy;
(dy, dyFee) = _calculateWithdrawOneToken(
self,
tokenAmount,
tokenIndex,
_updateBaseVirtualPrice(metaSwapStorage),
totalSupply
);
require(dy >= minAmount, "dy < minAmount");
// Update balances array
self.balances[tokenIndex] = self.balances[tokenIndex].sub(
dy.add(dyFee.mul(self.adminFee).div(FEE_DENOMINATOR))
);
// Burn the associated LP token from the caller and send the desired token
lpToken.burnFrom(msg.sender, tokenAmount);
self.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 metaSwapStorage MetaSwap 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(
SwapUtils.Swap storage self,
MetaSwap storage metaSwapStorage,
uint256[] memory amounts,
uint256 maxBurnAmount
) public returns (uint256) {
// Using this struct to avoid stack too deep error
ManageLiquidityInfo memory v = ManageLiquidityInfo(
0,
0,
0,
self.lpToken,
0,
self._getAPrecise(),
_updateBaseVirtualPrice(metaSwapStorage),
self.tokenPrecisionMultipliers,
self.balances
);
v.totalSupply = v.lpToken.totalSupply();
require(
amounts.length == v.newBalances.length,
"Amounts should match pool tokens"
);
require(maxBurnAmount != 0, "Must burn more than 0");
uint256 feePerToken = SwapUtils._feePerToken(
self.swapFee,
v.newBalances.length
);
// Calculate how much LPToken should be burned
uint256[] memory fees = new uint256[](v.newBalances.length);
{
uint256[] memory balances1 = new uint256[](v.newBalances.length);
v.d0 = SwapUtils.getD(
_xp(
v.newBalances,
v.tokenPrecisionMultipliers,
v.baseVirtualPrice
),
v.preciseA
);
for (uint256 i = 0; i < v.newBalances.length; i++) {
balances1[i] = v.newBalances[i].sub(
amounts[i],
"Cannot withdraw more than available"
);
}
v.d1 = SwapUtils.getD(
_xp(balances1, v.tokenPrecisionMultipliers, v.baseVirtualPrice),
v.preciseA
);
for (uint256 i = 0; i < v.newBalances.length; i++) {
uint256 idealBalance = v.d1.mul(v.newBalances[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 = SwapUtils.getD(
_xp(balances1, v.tokenPrecisionMultipliers, v.baseVirtualPrice),
v.preciseA
);
}
uint256 tokenAmount = v.d0.sub(v.d2).mul(v.totalSupply).div(v.d0);
require(tokenAmount != 0, "Burnt amount cannot be zero");
// Scale up by withdraw fee
tokenAmount = tokenAmount.add(1);
// Check for max burn amount
require(tokenAmount <= maxBurnAmount, "tokenAmount > maxBurnAmount");
// Burn the calculated amount of LPToken from the caller and send the desired tokens
v.lpToken.burnFrom(msg.sender, tokenAmount);
for (uint256 i = 0; i < v.newBalances.length; i++) {
self.pooledTokens[i].safeTransfer(msg.sender, amounts[i]);
}
emit RemoveLiquidityImbalance(
msg.sender,
amounts,
fees,
v.d1,
v.totalSupply.sub(tokenAmount)
);
return tokenAmount;
}
/**
* @notice Determines if the stored value of base Swap's virtual price is expired.
* If the last update was past the BASE_CACHE_EXPIRE_TIME, then update the stored value.
*
* @param metaSwapStorage MetaSwap struct to read from and write to
* @return base Swap's virtual price
*/
function _updateBaseVirtualPrice(MetaSwap storage metaSwapStorage)
internal
returns (uint256)
{
if (
block.timestamp >
metaSwapStorage.baseCacheLastUpdated + BASE_CACHE_EXPIRE_TIME
) {
// When the cache is expired, update it
uint256 baseVirtualPrice = ISwap(metaSwapStorage.baseSwap)
.getVirtualPrice();
metaSwapStorage.baseVirtualPrice = baseVirtualPrice;
metaSwapStorage.baseCacheLastUpdated = block.timestamp;
return baseVirtualPrice;
} else {
return metaSwapStorage.baseVirtualPrice;
}
}
}{
"evmVersion": "istanbul",
"libraries": {
"contracts/meta/MetaSwap.sol:MetaSwap": {
"SwapUtils": "0x2069043d7556B1207a505eb459D18d908DF29b55",
"MetaSwapUtils": "0x88Cc4aA0dd6Cf126b00C012dDa9f6F4fd9388b17",
"AmplificationUtils": "0x3661D0F70e7f3EC418321A57FD62d691a09b490B"
}
},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 10000
},
"remappings": [],
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}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":false,"internalType":"uint256","name":"newWithdrawFee","type":"uint256"}],"name":"NewWithdrawFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"initialTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"futureTime","type":"uint256"}],"name":"RampA","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":false,"internalType":"uint256","name":"currentA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"}],"name":"StopRampA","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"},{"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":"TokenSwapUnderlying","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"minToMint","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"calculateRemoveLiquidity","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint8","name":"tokenIndex","type":"uint8"}],"name":"calculateRemoveLiquidityOneToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"tokenIndexFrom","type":"uint8"},{"internalType":"uint8","name":"tokenIndexTo","type":"uint8"},{"internalType":"uint256","name":"dx","type":"uint256"}],"name":"calculateSwap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"tokenIndexFrom","type":"uint8"},{"internalType":"uint8","name":"tokenIndexTo","type":"uint8"},{"internalType":"uint256","name":"dx","type":"uint256"}],"name":"calculateSwapUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bool","name":"deposit","type":"bool"}],"name":"calculateTokenAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getA","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAPrecise","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getAdminBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"index","type":"uint8"}],"name":"getToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"index","type":"uint8"}],"name":"getTokenBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"getTokenIndex","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVirtualPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"_pooledTokens","type":"address[]"},{"internalType":"uint8[]","name":"decimals","type":"uint8[]"},{"internalType":"string","name":"lpTokenName","type":"string"},{"internalType":"string","name":"lpTokenSymbol","type":"string"},{"internalType":"uint256","name":"_a","type":"uint256"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"uint256","name":"_adminFee","type":"uint256"},{"internalType":"address","name":"lpTokenTargetAddress","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"_pooledTokens","type":"address[]"},{"internalType":"uint8[]","name":"decimals","type":"uint8[]"},{"internalType":"string","name":"lpTokenName","type":"string"},{"internalType":"string","name":"lpTokenSymbol","type":"string"},{"internalType":"uint256","name":"_a","type":"uint256"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"uint256","name":"_adminFee","type":"uint256"},{"internalType":"address","name":"lpTokenTargetAddress","type":"address"},{"internalType":"contract ISwap","name":"baseSwap","type":"address"}],"name":"initializeMetaSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"metaSwapStorage","outputs":[{"internalType":"contract ISwap","name":"baseSwap","type":"address"},{"internalType":"uint256","name":"baseVirtualPrice","type":"uint256"},{"internalType":"uint256","name":"baseCacheLastUpdated","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"futureA","type":"uint256"},{"internalType":"uint256","name":"futureTime","type":"uint256"}],"name":"rampA","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256[]","name":"minAmounts","type":"uint256[]"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"maxBurnAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityImbalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint8","name":"tokenIndex","type":"uint8"},{"internalType":"uint256","name":"minAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityOneToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newAdminFee","type":"uint256"}],"name":"setAdminFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newSwapFee","type":"uint256"}],"name":"setSwapFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stopRampA","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"tokenIndexFrom","type":"uint8"},{"internalType":"uint8","name":"tokenIndexTo","type":"uint8"},{"internalType":"uint256","name":"dx","type":"uint256"},{"internalType":"uint256","name":"minDy","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapStorage","outputs":[{"internalType":"uint256","name":"initialA","type":"uint256"},{"internalType":"uint256","name":"futureA","type":"uint256"},{"internalType":"uint256","name":"initialATime","type":"uint256"},{"internalType":"uint256","name":"futureATime","type":"uint256"},{"internalType":"uint256","name":"swapFee","type":"uint256"},{"internalType":"uint256","name":"adminFee","type":"uint256"},{"internalType":"contract LPToken","name":"lpToken","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"tokenIndexFrom","type":"uint8"},{"internalType":"uint8","name":"tokenIndexTo","type":"uint8"},{"internalType":"uint256","name":"dx","type":"uint256"},{"internalType":"uint256","name":"minDy","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAdminFees","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
608060405234801561001057600080fd5b50614124806100206000396000f3fe608060405234801561001057600080fd5b506004361061020b5760003560e01c806378e0fae81161012a578063a95b089f116100bd578063e25aa5fa1161008c578063ef0a712f11610071578063ef0a712f14610bf3578063f2fad2b614610c10578063f2fde38b14610c2d5761020b565b8063e25aa5fa14610b79578063e6ab280614610b815761020b565b8063a95b089f146108e9578063b28cb6dc14610919578063c4db7fa014610b69578063d46300fd14610b715761020b565b80638beb60b6116100f95780638beb60b6146108685780638da5cb5b14610885578063916955861461088d57806391ceb3eb146108c95761020b565b806378e0fae81461077257806382b86600146107ae5780638456cb59146107ea57806384cdd9bc146107f25761020b565b80633f4ba83a116101a25780635fd65f0f116101715780635fd65f0f146106b557806366c0bd24146106fe578063715018a61461073a57806375d8e3e4146107425761020b565b80633f4ba83a146105f85780634d49e87d14610600578063593d132c146106765780635c975abb146106995761020b565b806331cd52b0116101de57806331cd52b0146104bc578063342a87a11461058357806334e19907146105a95780633e3a1560146105c65761020b565b80630419b45a146102105780630ba819591461021a578063118e1c77146102345780632d74d4e91461048c575b600080fd5b610218610c53565b005b610222610d49565b60408051918252519081900360200190f35b610218600480360361012081101561024b57600080fd5b81019060208101813564010000000081111561026657600080fd5b82018360208201111561027857600080fd5b8035906020019184602083028401116401000000008311171561029a57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092959493602081019350359150506401000000008111156102ea57600080fd5b8201836020820111156102fc57600080fd5b8035906020019184602083028401116401000000008311171561031e57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929594936020810193503591505064010000000081111561036e57600080fd5b82018360208201111561038057600080fd5b803590602001918460018302840111640100000000831117156103a257600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092959493602081019350359150506401000000008111156103f557600080fd5b82018360208201111561040757600080fd5b8035906020019184600183028401116401000000008311171561042957600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550508235935050506020810135906040810135906001600160a01b0360608201358116916080013516610dce565b6104946111a7565b604080516001600160a01b039094168452602084019290925282820152519081900360600190f35b610533600480360360608110156104d257600080fd5b813591908101906040810160208201356401000000008111156104f457600080fd5b82018360208201111561050657600080fd5b8035906020019184602083028401116401000000008311171561052857600080fd5b9193509150356111bf565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561056f578181015183820152602001610557565b505050509050019250505060405180910390f35b6102226004803603604081101561059957600080fd5b508035906020013560ff166113fc565b610218600480360360208110156105bf57600080fd5b50356114b7565b610222600480360360808110156105dc57600080fd5b5080359060ff60208201351690604081013590606001356115b8565b61021861178b565b6102226004803603606081101561061657600080fd5b81019060208101813564010000000081111561063157600080fd5b82018360208201111561064357600080fd5b8035906020019184602083028401116401000000008311171561066557600080fd5b919350915080359060200135611809565b6102186004803603604081101561068c57600080fd5b50803590602001356119b0565b6106a1611ab9565b604080519115158252519081900360200190f35b6106bd611ac2565b604080519788526020880196909652868601949094526060860192909252608085015260a08401526001600160a01b031660c0830152519081900360e00190f35b6107246004803603602081101561071457600080fd5b50356001600160a01b0316611ae3565b6040805160ff9092168252519081900360200190f35b610218611b70565b6102226004803603606081101561075857600080fd5b5060ff813581169160208101359091169060400135611c46565b610222600480360360a081101561078857600080fd5b5060ff813581169160208101359091169060408101359060608101359060800135611d0a565b6107ce600480360360208110156107c457600080fd5b503560ff16611ee4565b604080516001600160a01b039092168252519081900360200190f35b610218611f6c565b6102226004803603606081101561080857600080fd5b81019060208101813564010000000081111561082357600080fd5b82018360208201111561083557600080fd5b8035906020019184602083028401116401000000008311171561085757600080fd5b919350915080359060200135611fe8565b6102186004803603602081101561087e57600080fd5b503561218f565b6107ce612275565b610222600480360360a08110156108a357600080fd5b5060ff813581169160208101359091169060408101359060608101359060800135612284565b610222600480360360208110156108df57600080fd5b503560ff16612424565b610222600480360360608110156108ff57600080fd5b5060ff8135811691602081013590911690604001356124a3565b610218600480360361010081101561093057600080fd5b81019060208101813564010000000081111561094b57600080fd5b82018360208201111561095d57600080fd5b8035906020019184602083028401116401000000008311171561097f57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092959493602081019350359150506401000000008111156109cf57600080fd5b8201836020820111156109e157600080fd5b80359060200191846020830284011164010000000083111715610a0357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050640100000000811115610a5357600080fd5b820183602082011115610a6557600080fd5b80359060200191846001830284011164010000000083111715610a8757600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050640100000000811115610ada57600080fd5b820183602082011115610aec57600080fd5b80359060200191846001830284011164010000000083111715610b0e57600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955050823593505050602081013590604081013590606001356001600160a01b0316612531565b610218612616565b6102226126f5565b610222612749565b61022260048036036040811015610b9757600080fd5b810190602081018135640100000000811115610bb257600080fd5b820183602082011115610bc457600080fd5b80359060200191846020830284011164010000000083111715610be657600080fd5b91935091503515156127a4565b61022260048036036020811015610c0957600080fd5b5035612840565b61053360048036036020811015610c2657600080fd5b50356128ce565b61021860048036036020811015610c4357600080fd5b50356001600160a01b0316612a18565b610c5b612b45565b6001600160a01b0316610c6c612275565b6001600160a01b031614610cc7576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b732069043d7556b1207a505eb459d18d908df29b556324c5c75160c9610ceb612275565b6040518363ffffffff1660e01b815260040180838152602001826001600160a01b031681526020019250505060006040518083038186803b158015610d2f57600080fd5b505af4158015610d43573d6000803e3d6000fd5b50505050565b600060c9733661d0f70e7f3ec418321a57fd62d691a09b490b63c9b64dcb90916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015610d9d57600080fd5b505af4158015610db1573d6000803e3d6000fd5b505050506040513d6020811015610dc757600080fd5b5051905090565b600054610100900460ff1680610de75750610de7612b49565b80610df5575060005460ff16155b610e305760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff16158015610e5b576000805460ff1961ff0019909116610100171660011790555b610e6b8a8a8a8a8a8a8a8a612b5a565b60d480547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038416908117909155604080517fe25aa5fa000000000000000000000000000000000000000000000000000000008152905163e25aa5fa91600480820192602092909190829003018186803b158015610ef057600080fd5b505afa158015610f04573d6000803e3d6000fd5b505050506040513d6020811015610f1a57600080fd5b505160d5554260d65560005b60208160ff16101561104157826001600160a01b03166382b86600826040518263ffffffff1660e01b8152600401808260ff16815260200191505060206040518083038186803b158015610f7957600080fd5b505afa925050508015610f9e57506040513d6020811015610f9957600080fd5b505160015b610fa757611041565b60d780546001810182556000919091527f8a012a6de2943a5aa4d77acf5e695d4456760a3f1f30a5d6dc2079599187a0710180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03831690811790915561103890857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613355565b50600101610f26565b60018160ff16116110835760405162461bcd60e51b815260040180806020018281038252602481526020018061406b6024913960400191505060405180910390fd5b5060008a60018c51038151811061109657fe5b60200260200101519050826001600160a01b0316816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156110e357600080fd5b505afa1580156110f7573d6000803e3d6000fd5b505050506040513d602081101561110d57600080fd5b50516001600160a01b0316146111545760405162461bcd60e51b81526004018080602001828103825260248152602001806140266024913960400191505060405180910390fd5b6111886001600160a01b038216847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613355565b50801561119b576000805461ff00191690555b50505050505050505050565b60d45460d55460d6546001600160a01b039092169183565b606060026097541415611219576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026097558142811015611274576040805162461bcd60e51b815260206004820152601060248201527f446561646c696e65206e6f74206d657400000000000000000000000000000000604482015290519081900360640190fd5b60c9732069043d7556b1207a505eb459d18d908df29b556373fd6b3e90918888886040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925060200280828437600081840152601f19601f8201169050808301925050509550505050505060006040518083038186803b15801561130557600080fd5b505af4158015611319573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052602081101561136057600080fd5b810190808051604051939291908464010000000082111561138057600080fd5b90830190602082018581111561139557600080fd5b82518660208202830111640100000000821117156113b257600080fd5b82525081516020918201928201910280838360005b838110156113df5781810151838201526020016113c7565b505050509050016040525050509150506001609755949350505050565b604080517ff6ada8c200000000000000000000000000000000000000000000000000000000815260c9600482015260d460248201526044810184905260ff8316606482015290516000917388cc4aa0dd6cf126b00c012dda9f6f4fd9388b179163f6ada8c291608480820192602092909190829003018186803b15801561148257600080fd5b505af4158015611496573d6000803e3d6000fd5b505050506040513d60208110156114ac57600080fd5b505190505b92915050565b6114bf612b45565b6001600160a01b03166114d0612275565b6001600160a01b03161461152b576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b604080517f467e186c00000000000000000000000000000000000000000000000000000000815260c96004820152602481018390529051732069043d7556b1207a505eb459d18d908df29b559163467e186c916044808301926000929190829003018186803b15801561159d57600080fd5b505af41580156115b1573d6000803e3d6000fd5b5050505050565b600060026097541415611612576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b600260975561161f611ab9565b15611671576040805162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015290519081900360640190fd5b81804211156116c7576040805162461bcd60e51b815260206004820152601060248201527f446561646c696e65206e6f74206d657400000000000000000000000000000000604482015290519081900360640190fd5b604080517fc1a6627300000000000000000000000000000000000000000000000000000000815260c9600482015260d460248201526044810188905260ff871660648201526084810186905290517388cc4aa0dd6cf126b00c012dda9f6f4fd9388b179163c1a662739160a4808301926020929190829003018186803b15801561175057600080fd5b505af4158015611764573d6000803e3d6000fd5b505050506040513d602081101561177a57600080fd5b505160016097559695505050505050565b611793612b45565b6001600160a01b03166117a4612275565b6001600160a01b0316146117ff576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6118076134b4565b565b600060026097541415611863576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002609755611870611ab9565b156118c2576040805162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015290519081900360640190fd5b8180421115611918576040805162461bcd60e51b815260206004820152601060248201527f446561646c696e65206e6f74206d657400000000000000000000000000000000604482015290519081900360640190fd5b7388cc4aa0dd6cf126b00c012dda9f6f4fd9388b1763d1ab4b4a60c960d48989896040518663ffffffff1660e01b815260040180868152602001858152602001806020018381526020018281038252858582818152602001925060200280828437600081840152601f19601f820116905080830192505050965050505050505060206040518083038186803b15801561175057600080fd5b6119b8612b45565b6001600160a01b03166119c9612275565b6001600160a01b031614611a24576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b604080517f58fdd79b00000000000000000000000000000000000000000000000000000000815260c9600482015260248101849052604481018390529051733661d0f70e7f3ec418321a57fd62d691a09b490b916358fdd79b916064808301926000929190829003018186803b158015611a9d57600080fd5b505af4158015611ab1573d6000803e3d6000fd5b505050505050565b60655460ff1690565b60c95460ca5460cb5460cc5460cd5460ce5460cf546001600160a01b031687565b6001600160a01b038116600081815260d36020526040812054909160ff90911690611b0d82611ee4565b6001600160a01b031614611b68576040805162461bcd60e51b815260206004820152601460248201527f546f6b656e20646f6573206e6f74206578697374000000000000000000000000604482015290519081900360640190fd5b90505b919050565b611b78612b45565b6001600160a01b0316611b89612275565b6001600160a01b031614611be4576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6033546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3603380547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b604080517fd45757ba00000000000000000000000000000000000000000000000000000000815260c9600482015260d4602482015260ff8086166044830152841660648201526084810183905290516000917388cc4aa0dd6cf126b00c012dda9f6f4fd9388b179163d45757ba9160a480820192602092909190829003018186803b158015611cd457600080fd5b505af4158015611ce8573d6000803e3d6000fd5b505050506040513d6020811015611cfe57600080fd5b505190505b9392505050565b600060026097541415611d64576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002609755611d71611ab9565b15611dc3576040805162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015290519081900360640190fd5b8180421115611e19576040805162461bcd60e51b815260206004820152601060248201527f446561646c696e65206e6f74206d657400000000000000000000000000000000604482015290519081900360640190fd5b604080517e80247a00000000000000000000000000000000000000000000000000000000815260c9600482015260d4602482015260ff808a166044830152881660648201526084810187905260a4810186905290517388cc4aa0dd6cf126b00c012dda9f6f4fd9388b17916280247a9160c4808301926020929190829003018186803b158015611ea857600080fd5b505af4158015611ebc573d6000803e3d6000fd5b505050506040513d6020811015611ed257600080fd5b50516001609755979650505050505050565b60d05460009060ff831610611f40576040805162461bcd60e51b815260206004820152600c60248201527f4f7574206f662072616e67650000000000000000000000000000000000000000604482015290519081900360640190fd5b60d0805460ff8416908110611f5157fe5b6000918252602090912001546001600160a01b031692915050565b611f74612b45565b6001600160a01b0316611f85612275565b6001600160a01b031614611fe0576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b61180761355d565b600060026097541415612042576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b600260975561204f611ab9565b156120a1576040805162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015290519081900360640190fd5b81804211156120f7576040805162461bcd60e51b815260206004820152601060248201527f446561646c696e65206e6f74206d657400000000000000000000000000000000604482015290519081900360640190fd5b7388cc4aa0dd6cf126b00c012dda9f6f4fd9388b17637e97165660c960d48989896040518663ffffffff1660e01b815260040180868152602001858152602001806020018381526020018281038252858582818152602001925060200280828437600081840152601f19601f820116905080830192505050965050505050505060206040518083038186803b15801561175057600080fd5b612197612b45565b6001600160a01b03166121a8612275565b6001600160a01b031614612203576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b604080517f7046727600000000000000000000000000000000000000000000000000000000815260c96004820152602481018390529051732069043d7556b1207a505eb459d18d908df29b55916370467276916044808301926000929190829003018186803b15801561159d57600080fd5b6033546001600160a01b031690565b6000600260975414156122de576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026097556122eb611ab9565b1561233d576040805162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015290519081900360640190fd5b8180421115612393576040805162461bcd60e51b815260206004820152601060248201527f446561646c696e65206e6f74206d657400000000000000000000000000000000604482015290519081900360640190fd5b604080517f98414eed00000000000000000000000000000000000000000000000000000000815260c9600482015260d4602482015260ff808a166044830152881660648201526084810187905260a4810186905290517388cc4aa0dd6cf126b00c012dda9f6f4fd9388b17916398414eed9160c4808301926020929190829003018186803b158015611ea857600080fd5b60d05460009060ff831610612480576040805162461bcd60e51b815260206004820152601260248201527f496e646578206f7574206f662072616e67650000000000000000000000000000604482015290519081900360640190fd5b60d2805460ff841690811061249157fe5b90600052602060002001549050919050565b604080517f5afb90ff00000000000000000000000000000000000000000000000000000000815260c9600482015260d4602482015260ff8086166044830152841660648201526084810183905290516000917388cc4aa0dd6cf126b00c012dda9f6f4fd9388b1791635afb90ff9160a480820192602092909190829003018186803b158015611cd457600080fd5b600054610100900460ff168061254a575061254a612b49565b80612558575060005460ff16155b6125935760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff161580156125be576000805460ff1961ff0019909116610100171660011790555b6040805162461bcd60e51b815260206004820181905260248201527f75736520696e697469616c697a654d65746153776170282920696e7374656164604482015290519081900360640190fd5b505050505050505050565b61261e612b45565b6001600160a01b031661262f612275565b6001600160a01b03161461268a576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b604080517ff14e211e00000000000000000000000000000000000000000000000000000000815260c960048201529051733661d0f70e7f3ec418321a57fd62d691a09b490b9163f14e211e916024808301926000929190829003018186803b158015610d2f57600080fd5b600060c9733661d0f70e7f3ec418321a57fd62d691a09b490b63b0a14cfc90916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015610d9d57600080fd5b60007388cc4aa0dd6cf126b00c012dda9f6f4fd9388b1763e9d4d07760c960d46040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b158015610d9d57600080fd5b60007388cc4aa0dd6cf126b00c012dda9f6f4fd9388b176378b0bcbe60c960d48787876040518663ffffffff1660e01b8152600401808681526020018581526020018060200183151581526020018281038252858582818152602001925060200280828437600081840152601f19601f820116905080830192505050965050505050505060206040518083038186803b158015611cd457600080fd5b600060c9732069043d7556b1207a505eb459d18d908df29b55637d0481609091846040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561289c57600080fd5b505af41580156128b0573d6000803e3d6000fd5b505050506040513d60208110156128c657600080fd5b505192915050565b606060c9732069043d7556b1207a505eb459d18d908df29b556370703e4a9091846040518363ffffffff1660e01b8152600401808381526020018281526020019250505060006040518083038186803b15801561292a57600080fd5b505af415801561293e573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052602081101561298557600080fd5b81019080805160405193929190846401000000008211156129a557600080fd5b9083019060208201858111156129ba57600080fd5b82518660208202830111640100000000821117156129d757600080fd5b82525081516020918201928201910280838360005b83811015612a045781810151838201526020016129ec565b505050509050016040525050509050919050565b612a20612b45565b6001600160a01b0316612a31612275565b6001600160a01b031614612a8c576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b038116612ad15760405162461bcd60e51b8152600401808060200182810382526026815260200180613fac6026913960400191505060405180910390fd5b6033546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3603380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b3390565b6000612b54306135ed565b15905090565b600054610100900460ff1680612b735750612b73612b49565b80612b81575060005460ff16155b612bbc5760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff16158015612be7576000805460ff1961ff0019909116610100171660011790555b612bef6135f3565b612bf76136ad565b6001895111612c4d576040805162461bcd60e51b815260206004820152601960248201527f5f706f6f6c6564546f6b656e732e6c656e677468203c3d203100000000000000604482015290519081900360640190fd5b602089511115612ca4576040805162461bcd60e51b815260206004820152601960248201527f5f706f6f6c6564546f6b656e732e6c656e677468203e20333200000000000000604482015290519081900360640190fd5b8751895114612cfa576040805162461bcd60e51b815260206004820152601f60248201527f5f706f6f6c6564546f6b656e7320646563696d616c73206d69736d6174636800604482015290519081900360640190fd5b6060885167ffffffffffffffff81118015612d1457600080fd5b50604051908082528060200260200182016040528015612d3e578160200160208202803683370190505b50905060005b8a518160ff161015612fbc5760ff811615612e2d5760d360008c8360ff1681518110612d6c57fe5b6020908102919091018101516001600160a01b031682528101919091526040016000205460ff16158015612ddc57508a8160ff1681518110612daa57fe5b60200260200101516001600160a01b03168b600081518110612dc857fe5b60200260200101516001600160a01b031614155b612e2d576040805162461bcd60e51b815260206004820152601060248201527f4475706c696361746520746f6b656e7300000000000000000000000000000000604482015290519081900360640190fd5b60006001600160a01b03168b8260ff1681518110612e4757fe5b60200260200101516001600160a01b03161415612eab576040805162461bcd60e51b815260206004820152601d60248201527f546865203020616464726573732069736e277420616e204552432d3230000000604482015290519081900360640190fd5b601260ff168a8260ff1681518110612ebf57fe5b602002602001015160ff161115612f1d576040805162461bcd60e51b815260206004820152601a60248201527f546f6b656e20646563696d616c732065786365656473206d6178000000000000604482015290519081900360640190fd5b612f4d8a8260ff1681518110612f2f57fe5b602002602001015160ff16601260ff1661374290919063ffffffff16565b600a0a828260ff1681518110612f5f57fe5b6020026020010181815250508060d360008d8460ff1681518110612f7f57fe5b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff191660ff92909216919091179055600101612d44565b50620f42408610613014576040805162461bcd60e51b815260206004820152601260248201527f5f612065786365656473206d6178696d756d0000000000000000000000000000604482015290519081900360640190fd5b6305f5e100851061306c576040805162461bcd60e51b815260206004820152601460248201527f5f6665652065786365656473206d6178696d756d000000000000000000000000604482015290519081900360640190fd5b6402540be40084106130c5576040805162461bcd60e51b815260206004820152601960248201527f5f61646d696e4665652065786365656473206d6178696d756d00000000000000604482015290519081900360640190fd5b60006130d08461379f565b9050806001600160a01b0316634cd88b768a8a6040518363ffffffff1660e01b8152600401808060200180602001838103835285818151815260200191508051906020019080838360005b8381101561313357818101518382015260200161311b565b50505050905090810190601f1680156131605780820380516001836020036101000a031916815260200191505b50838103825284518152845160209182019186019080838360005b8381101561319357818101518382015260200161317b565b50505050905090810190601f1680156131c05780820380516001836020036101000a031916815260200191505b50945050505050602060405180830381600087803b1580156131e157600080fd5b505af11580156131f5573d6000803e3d6000fd5b505050506040513d602081101561320b57600080fd5b505161325e576040805162461bcd60e51b815260206004820152601c60248201527f636f756c64206e6f7420696e6974206c70546f6b656e20636c6f6e6500000000604482015290519081900360640190fd5b60cf80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383161790558a516132a49060d09060208e0190613e9b565b5081516132b89060d1906020850190613f18565b508a5167ffffffffffffffff811180156132d157600080fd5b506040519080825280602002602001820160405280156132fb578160200160208202803683370190505b5080516133109160d291602090910190613f18565b5061331c87606461385a565b60c95561332a87606461385a565b60ca55505060cd84905560ce839055801561260b576000805461ff0019169055505050505050505050565b8015806133f45750604080517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b1580156133c657600080fd5b505afa1580156133da573d6000803e3d6000fd5b505050506040513d60208110156133f057600080fd5b5051155b61342f5760405162461bcd60e51b81526004018080602001828103825260368152602001806140b96036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b3000000000000000000000000000000000000000000000000000000001790526134af9084906138b3565b505050565b6134bc611ab9565b61350d576040805162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f7420706175736564000000000000000000000000604482015290519081900360640190fd5b6065805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa613540612b45565b604080516001600160a01b039092168252519081900360200190a1565b613565611ab9565b156135b7576040805162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015290519081900360640190fd5b6065805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258613540612b45565b3b151590565b600054610100900460ff168061360c575061360c612b49565b8061361a575060005460ff16155b6136555760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff16158015613680576000805460ff1961ff0019909116610100171660011790555b613688613964565b613690613a04565b613698613b15565b80156136aa576000805461ff00191690555b50565b600054610100900460ff16806136c657506136c6612b49565b806136d4575060005460ff16155b61370f5760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff1615801561373a576000805460ff1961ff0019909116610100171660011790555b613698613bc0565b600082821115613799576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60006040517f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000081528260601b60148201527f5af43d82803e903d91602b57fd5bf3000000000000000000000000000000000060288201526037816000f09150506001600160a01b038116611b6b576040805162461bcd60e51b815260206004820152601660248201527f455243313136373a20637265617465206661696c656400000000000000000000604482015290519081900360640190fd5b600082613869575060006114b1565b8282028284828161387657fe5b0414611d035760405162461bcd60e51b815260040180806020018281038252602181526020018061404a6021913960400191505060405180910390fd5b6060613908826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613c669092919063ffffffff16565b8051909150156134af5780806020019051602081101561392757600080fd5b50516134af5760405162461bcd60e51b815260040180806020018281038252602a81526020018061408f602a913960400191505060405180910390fd5b600054610100900460ff168061397d575061397d612b49565b8061398b575060005460ff16155b6139c65760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff16158015613698576000805460ff1961ff00199091166101001716600117905580156136aa576000805461ff001916905550565b600054610100900460ff1680613a1d5750613a1d612b49565b80613a2b575060005460ff16155b613a665760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff16158015613a91576000805460ff1961ff0019909116610100171660011790555b6000613a9b612b45565b603380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a35080156136aa576000805461ff001916905550565b600054610100900460ff1680613b2e5750613b2e612b49565b80613b3c575060005460ff16155b613b775760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff16158015613ba2576000805460ff1961ff0019909116610100171660011790555b6065805460ff1916905580156136aa576000805461ff001916905550565b600054610100900460ff1680613bd95750613bd9612b49565b80613be7575060005460ff16155b613c225760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff16158015613c4d576000805460ff1961ff0019909116610100171660011790555b600160975580156136aa576000805461ff001916905550565b6060613c758484600085613c7d565b949350505050565b606082471015613cbe5760405162461bcd60e51b8152600401808060200182810382526026815260200180613fd26026913960400191505060405180910390fd5b613cc7856135ed565b613d18576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b60208310613d7557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101613d38565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114613dd7576040519150601f19603f3d011682016040523d82523d6000602084013e613ddc565b606091505b5091509150613dec828286613df7565b979650505050505050565b60608315613e06575081611d03565b825115613e165782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015613e60578181015183820152602001613e48565b50505050905090810190601f168015613e8d5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b828054828255906000526020600020908101928215613f08579160200282015b82811115613f0857825182547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03909116178255602090920191600190910190613ebb565b50613f14929150613f5f565b5090565b828054828255906000526020600020908101928215613f53579160200282015b82811115613f53578251825591602001919060010190613f38565b50613f14929150613f96565b5b80821115613f145780547fffffffffffffffffffffffff0000000000000000000000000000000000000000168155600101613f60565b5b80821115613f145760008155600101613f9756fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c496e697469616c697a61626c653a20636f6e747261637420697320616c726561647920696e697469616c697a6564626173654c50546f6b656e206973206e6f74206f776e6564206279206261736553776170536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f776261736553776170206d75737420706f6f6c206174206c65617374203220746f6b656e735361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e6365a2646970667358221220077a4a4d88a301ee87d30544652bdbf9fc343f9acdffcc5a0b8b2add767af86f64736f6c634300060c0033
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061020b5760003560e01c806378e0fae81161012a578063a95b089f116100bd578063e25aa5fa1161008c578063ef0a712f11610071578063ef0a712f14610bf3578063f2fad2b614610c10578063f2fde38b14610c2d5761020b565b8063e25aa5fa14610b79578063e6ab280614610b815761020b565b8063a95b089f146108e9578063b28cb6dc14610919578063c4db7fa014610b69578063d46300fd14610b715761020b565b80638beb60b6116100f95780638beb60b6146108685780638da5cb5b14610885578063916955861461088d57806391ceb3eb146108c95761020b565b806378e0fae81461077257806382b86600146107ae5780638456cb59146107ea57806384cdd9bc146107f25761020b565b80633f4ba83a116101a25780635fd65f0f116101715780635fd65f0f146106b557806366c0bd24146106fe578063715018a61461073a57806375d8e3e4146107425761020b565b80633f4ba83a146105f85780634d49e87d14610600578063593d132c146106765780635c975abb146106995761020b565b806331cd52b0116101de57806331cd52b0146104bc578063342a87a11461058357806334e19907146105a95780633e3a1560146105c65761020b565b80630419b45a146102105780630ba819591461021a578063118e1c77146102345780632d74d4e91461048c575b600080fd5b610218610c53565b005b610222610d49565b60408051918252519081900360200190f35b610218600480360361012081101561024b57600080fd5b81019060208101813564010000000081111561026657600080fd5b82018360208201111561027857600080fd5b8035906020019184602083028401116401000000008311171561029a57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092959493602081019350359150506401000000008111156102ea57600080fd5b8201836020820111156102fc57600080fd5b8035906020019184602083028401116401000000008311171561031e57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929594936020810193503591505064010000000081111561036e57600080fd5b82018360208201111561038057600080fd5b803590602001918460018302840111640100000000831117156103a257600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092959493602081019350359150506401000000008111156103f557600080fd5b82018360208201111561040757600080fd5b8035906020019184600183028401116401000000008311171561042957600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550508235935050506020810135906040810135906001600160a01b0360608201358116916080013516610dce565b6104946111a7565b604080516001600160a01b039094168452602084019290925282820152519081900360600190f35b610533600480360360608110156104d257600080fd5b813591908101906040810160208201356401000000008111156104f457600080fd5b82018360208201111561050657600080fd5b8035906020019184602083028401116401000000008311171561052857600080fd5b9193509150356111bf565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561056f578181015183820152602001610557565b505050509050019250505060405180910390f35b6102226004803603604081101561059957600080fd5b508035906020013560ff166113fc565b610218600480360360208110156105bf57600080fd5b50356114b7565b610222600480360360808110156105dc57600080fd5b5080359060ff60208201351690604081013590606001356115b8565b61021861178b565b6102226004803603606081101561061657600080fd5b81019060208101813564010000000081111561063157600080fd5b82018360208201111561064357600080fd5b8035906020019184602083028401116401000000008311171561066557600080fd5b919350915080359060200135611809565b6102186004803603604081101561068c57600080fd5b50803590602001356119b0565b6106a1611ab9565b604080519115158252519081900360200190f35b6106bd611ac2565b604080519788526020880196909652868601949094526060860192909252608085015260a08401526001600160a01b031660c0830152519081900360e00190f35b6107246004803603602081101561071457600080fd5b50356001600160a01b0316611ae3565b6040805160ff9092168252519081900360200190f35b610218611b70565b6102226004803603606081101561075857600080fd5b5060ff813581169160208101359091169060400135611c46565b610222600480360360a081101561078857600080fd5b5060ff813581169160208101359091169060408101359060608101359060800135611d0a565b6107ce600480360360208110156107c457600080fd5b503560ff16611ee4565b604080516001600160a01b039092168252519081900360200190f35b610218611f6c565b6102226004803603606081101561080857600080fd5b81019060208101813564010000000081111561082357600080fd5b82018360208201111561083557600080fd5b8035906020019184602083028401116401000000008311171561085757600080fd5b919350915080359060200135611fe8565b6102186004803603602081101561087e57600080fd5b503561218f565b6107ce612275565b610222600480360360a08110156108a357600080fd5b5060ff813581169160208101359091169060408101359060608101359060800135612284565b610222600480360360208110156108df57600080fd5b503560ff16612424565b610222600480360360608110156108ff57600080fd5b5060ff8135811691602081013590911690604001356124a3565b610218600480360361010081101561093057600080fd5b81019060208101813564010000000081111561094b57600080fd5b82018360208201111561095d57600080fd5b8035906020019184602083028401116401000000008311171561097f57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092959493602081019350359150506401000000008111156109cf57600080fd5b8201836020820111156109e157600080fd5b80359060200191846020830284011164010000000083111715610a0357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050640100000000811115610a5357600080fd5b820183602082011115610a6557600080fd5b80359060200191846001830284011164010000000083111715610a8757600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050640100000000811115610ada57600080fd5b820183602082011115610aec57600080fd5b80359060200191846001830284011164010000000083111715610b0e57600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955050823593505050602081013590604081013590606001356001600160a01b0316612531565b610218612616565b6102226126f5565b610222612749565b61022260048036036040811015610b9757600080fd5b810190602081018135640100000000811115610bb257600080fd5b820183602082011115610bc457600080fd5b80359060200191846020830284011164010000000083111715610be657600080fd5b91935091503515156127a4565b61022260048036036020811015610c0957600080fd5b5035612840565b61053360048036036020811015610c2657600080fd5b50356128ce565b61021860048036036020811015610c4357600080fd5b50356001600160a01b0316612a18565b610c5b612b45565b6001600160a01b0316610c6c612275565b6001600160a01b031614610cc7576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b732069043d7556b1207a505eb459d18d908df29b556324c5c75160c9610ceb612275565b6040518363ffffffff1660e01b815260040180838152602001826001600160a01b031681526020019250505060006040518083038186803b158015610d2f57600080fd5b505af4158015610d43573d6000803e3d6000fd5b50505050565b600060c9733661d0f70e7f3ec418321a57fd62d691a09b490b63c9b64dcb90916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015610d9d57600080fd5b505af4158015610db1573d6000803e3d6000fd5b505050506040513d6020811015610dc757600080fd5b5051905090565b600054610100900460ff1680610de75750610de7612b49565b80610df5575060005460ff16155b610e305760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff16158015610e5b576000805460ff1961ff0019909116610100171660011790555b610e6b8a8a8a8a8a8a8a8a612b5a565b60d480547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038416908117909155604080517fe25aa5fa000000000000000000000000000000000000000000000000000000008152905163e25aa5fa91600480820192602092909190829003018186803b158015610ef057600080fd5b505afa158015610f04573d6000803e3d6000fd5b505050506040513d6020811015610f1a57600080fd5b505160d5554260d65560005b60208160ff16101561104157826001600160a01b03166382b86600826040518263ffffffff1660e01b8152600401808260ff16815260200191505060206040518083038186803b158015610f7957600080fd5b505afa925050508015610f9e57506040513d6020811015610f9957600080fd5b505160015b610fa757611041565b60d780546001810182556000919091527f8a012a6de2943a5aa4d77acf5e695d4456760a3f1f30a5d6dc2079599187a0710180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03831690811790915561103890857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613355565b50600101610f26565b60018160ff16116110835760405162461bcd60e51b815260040180806020018281038252602481526020018061406b6024913960400191505060405180910390fd5b5060008a60018c51038151811061109657fe5b60200260200101519050826001600160a01b0316816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156110e357600080fd5b505afa1580156110f7573d6000803e3d6000fd5b505050506040513d602081101561110d57600080fd5b50516001600160a01b0316146111545760405162461bcd60e51b81526004018080602001828103825260248152602001806140266024913960400191505060405180910390fd5b6111886001600160a01b038216847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613355565b50801561119b576000805461ff00191690555b50505050505050505050565b60d45460d55460d6546001600160a01b039092169183565b606060026097541415611219576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026097558142811015611274576040805162461bcd60e51b815260206004820152601060248201527f446561646c696e65206e6f74206d657400000000000000000000000000000000604482015290519081900360640190fd5b60c9732069043d7556b1207a505eb459d18d908df29b556373fd6b3e90918888886040518563ffffffff1660e01b815260040180858152602001848152602001806020018281038252848482818152602001925060200280828437600081840152601f19601f8201169050808301925050509550505050505060006040518083038186803b15801561130557600080fd5b505af4158015611319573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052602081101561136057600080fd5b810190808051604051939291908464010000000082111561138057600080fd5b90830190602082018581111561139557600080fd5b82518660208202830111640100000000821117156113b257600080fd5b82525081516020918201928201910280838360005b838110156113df5781810151838201526020016113c7565b505050509050016040525050509150506001609755949350505050565b604080517ff6ada8c200000000000000000000000000000000000000000000000000000000815260c9600482015260d460248201526044810184905260ff8316606482015290516000917388cc4aa0dd6cf126b00c012dda9f6f4fd9388b179163f6ada8c291608480820192602092909190829003018186803b15801561148257600080fd5b505af4158015611496573d6000803e3d6000fd5b505050506040513d60208110156114ac57600080fd5b505190505b92915050565b6114bf612b45565b6001600160a01b03166114d0612275565b6001600160a01b03161461152b576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b604080517f467e186c00000000000000000000000000000000000000000000000000000000815260c96004820152602481018390529051732069043d7556b1207a505eb459d18d908df29b559163467e186c916044808301926000929190829003018186803b15801561159d57600080fd5b505af41580156115b1573d6000803e3d6000fd5b5050505050565b600060026097541415611612576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b600260975561161f611ab9565b15611671576040805162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015290519081900360640190fd5b81804211156116c7576040805162461bcd60e51b815260206004820152601060248201527f446561646c696e65206e6f74206d657400000000000000000000000000000000604482015290519081900360640190fd5b604080517fc1a6627300000000000000000000000000000000000000000000000000000000815260c9600482015260d460248201526044810188905260ff871660648201526084810186905290517388cc4aa0dd6cf126b00c012dda9f6f4fd9388b179163c1a662739160a4808301926020929190829003018186803b15801561175057600080fd5b505af4158015611764573d6000803e3d6000fd5b505050506040513d602081101561177a57600080fd5b505160016097559695505050505050565b611793612b45565b6001600160a01b03166117a4612275565b6001600160a01b0316146117ff576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6118076134b4565b565b600060026097541415611863576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002609755611870611ab9565b156118c2576040805162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015290519081900360640190fd5b8180421115611918576040805162461bcd60e51b815260206004820152601060248201527f446561646c696e65206e6f74206d657400000000000000000000000000000000604482015290519081900360640190fd5b7388cc4aa0dd6cf126b00c012dda9f6f4fd9388b1763d1ab4b4a60c960d48989896040518663ffffffff1660e01b815260040180868152602001858152602001806020018381526020018281038252858582818152602001925060200280828437600081840152601f19601f820116905080830192505050965050505050505060206040518083038186803b15801561175057600080fd5b6119b8612b45565b6001600160a01b03166119c9612275565b6001600160a01b031614611a24576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b604080517f58fdd79b00000000000000000000000000000000000000000000000000000000815260c9600482015260248101849052604481018390529051733661d0f70e7f3ec418321a57fd62d691a09b490b916358fdd79b916064808301926000929190829003018186803b158015611a9d57600080fd5b505af4158015611ab1573d6000803e3d6000fd5b505050505050565b60655460ff1690565b60c95460ca5460cb5460cc5460cd5460ce5460cf546001600160a01b031687565b6001600160a01b038116600081815260d36020526040812054909160ff90911690611b0d82611ee4565b6001600160a01b031614611b68576040805162461bcd60e51b815260206004820152601460248201527f546f6b656e20646f6573206e6f74206578697374000000000000000000000000604482015290519081900360640190fd5b90505b919050565b611b78612b45565b6001600160a01b0316611b89612275565b6001600160a01b031614611be4576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6033546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3603380547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b604080517fd45757ba00000000000000000000000000000000000000000000000000000000815260c9600482015260d4602482015260ff8086166044830152841660648201526084810183905290516000917388cc4aa0dd6cf126b00c012dda9f6f4fd9388b179163d45757ba9160a480820192602092909190829003018186803b158015611cd457600080fd5b505af4158015611ce8573d6000803e3d6000fd5b505050506040513d6020811015611cfe57600080fd5b505190505b9392505050565b600060026097541415611d64576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002609755611d71611ab9565b15611dc3576040805162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015290519081900360640190fd5b8180421115611e19576040805162461bcd60e51b815260206004820152601060248201527f446561646c696e65206e6f74206d657400000000000000000000000000000000604482015290519081900360640190fd5b604080517e80247a00000000000000000000000000000000000000000000000000000000815260c9600482015260d4602482015260ff808a166044830152881660648201526084810187905260a4810186905290517388cc4aa0dd6cf126b00c012dda9f6f4fd9388b17916280247a9160c4808301926020929190829003018186803b158015611ea857600080fd5b505af4158015611ebc573d6000803e3d6000fd5b505050506040513d6020811015611ed257600080fd5b50516001609755979650505050505050565b60d05460009060ff831610611f40576040805162461bcd60e51b815260206004820152600c60248201527f4f7574206f662072616e67650000000000000000000000000000000000000000604482015290519081900360640190fd5b60d0805460ff8416908110611f5157fe5b6000918252602090912001546001600160a01b031692915050565b611f74612b45565b6001600160a01b0316611f85612275565b6001600160a01b031614611fe0576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b61180761355d565b600060026097541415612042576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b600260975561204f611ab9565b156120a1576040805162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015290519081900360640190fd5b81804211156120f7576040805162461bcd60e51b815260206004820152601060248201527f446561646c696e65206e6f74206d657400000000000000000000000000000000604482015290519081900360640190fd5b7388cc4aa0dd6cf126b00c012dda9f6f4fd9388b17637e97165660c960d48989896040518663ffffffff1660e01b815260040180868152602001858152602001806020018381526020018281038252858582818152602001925060200280828437600081840152601f19601f820116905080830192505050965050505050505060206040518083038186803b15801561175057600080fd5b612197612b45565b6001600160a01b03166121a8612275565b6001600160a01b031614612203576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b604080517f7046727600000000000000000000000000000000000000000000000000000000815260c96004820152602481018390529051732069043d7556b1207a505eb459d18d908df29b55916370467276916044808301926000929190829003018186803b15801561159d57600080fd5b6033546001600160a01b031690565b6000600260975414156122de576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026097556122eb611ab9565b1561233d576040805162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015290519081900360640190fd5b8180421115612393576040805162461bcd60e51b815260206004820152601060248201527f446561646c696e65206e6f74206d657400000000000000000000000000000000604482015290519081900360640190fd5b604080517f98414eed00000000000000000000000000000000000000000000000000000000815260c9600482015260d4602482015260ff808a166044830152881660648201526084810187905260a4810186905290517388cc4aa0dd6cf126b00c012dda9f6f4fd9388b17916398414eed9160c4808301926020929190829003018186803b158015611ea857600080fd5b60d05460009060ff831610612480576040805162461bcd60e51b815260206004820152601260248201527f496e646578206f7574206f662072616e67650000000000000000000000000000604482015290519081900360640190fd5b60d2805460ff841690811061249157fe5b90600052602060002001549050919050565b604080517f5afb90ff00000000000000000000000000000000000000000000000000000000815260c9600482015260d4602482015260ff8086166044830152841660648201526084810183905290516000917388cc4aa0dd6cf126b00c012dda9f6f4fd9388b1791635afb90ff9160a480820192602092909190829003018186803b158015611cd457600080fd5b600054610100900460ff168061254a575061254a612b49565b80612558575060005460ff16155b6125935760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff161580156125be576000805460ff1961ff0019909116610100171660011790555b6040805162461bcd60e51b815260206004820181905260248201527f75736520696e697469616c697a654d65746153776170282920696e7374656164604482015290519081900360640190fd5b505050505050505050565b61261e612b45565b6001600160a01b031661262f612275565b6001600160a01b03161461268a576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b604080517ff14e211e00000000000000000000000000000000000000000000000000000000815260c960048201529051733661d0f70e7f3ec418321a57fd62d691a09b490b9163f14e211e916024808301926000929190829003018186803b158015610d2f57600080fd5b600060c9733661d0f70e7f3ec418321a57fd62d691a09b490b63b0a14cfc90916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015610d9d57600080fd5b60007388cc4aa0dd6cf126b00c012dda9f6f4fd9388b1763e9d4d07760c960d46040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b158015610d9d57600080fd5b60007388cc4aa0dd6cf126b00c012dda9f6f4fd9388b176378b0bcbe60c960d48787876040518663ffffffff1660e01b8152600401808681526020018581526020018060200183151581526020018281038252858582818152602001925060200280828437600081840152601f19601f820116905080830192505050965050505050505060206040518083038186803b158015611cd457600080fd5b600060c9732069043d7556b1207a505eb459d18d908df29b55637d0481609091846040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561289c57600080fd5b505af41580156128b0573d6000803e3d6000fd5b505050506040513d60208110156128c657600080fd5b505192915050565b606060c9732069043d7556b1207a505eb459d18d908df29b556370703e4a9091846040518363ffffffff1660e01b8152600401808381526020018281526020019250505060006040518083038186803b15801561292a57600080fd5b505af415801561293e573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052602081101561298557600080fd5b81019080805160405193929190846401000000008211156129a557600080fd5b9083019060208201858111156129ba57600080fd5b82518660208202830111640100000000821117156129d757600080fd5b82525081516020918201928201910280838360005b83811015612a045781810151838201526020016129ec565b505050509050016040525050509050919050565b612a20612b45565b6001600160a01b0316612a31612275565b6001600160a01b031614612a8c576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b038116612ad15760405162461bcd60e51b8152600401808060200182810382526026815260200180613fac6026913960400191505060405180910390fd5b6033546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3603380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b3390565b6000612b54306135ed565b15905090565b600054610100900460ff1680612b735750612b73612b49565b80612b81575060005460ff16155b612bbc5760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff16158015612be7576000805460ff1961ff0019909116610100171660011790555b612bef6135f3565b612bf76136ad565b6001895111612c4d576040805162461bcd60e51b815260206004820152601960248201527f5f706f6f6c6564546f6b656e732e6c656e677468203c3d203100000000000000604482015290519081900360640190fd5b602089511115612ca4576040805162461bcd60e51b815260206004820152601960248201527f5f706f6f6c6564546f6b656e732e6c656e677468203e20333200000000000000604482015290519081900360640190fd5b8751895114612cfa576040805162461bcd60e51b815260206004820152601f60248201527f5f706f6f6c6564546f6b656e7320646563696d616c73206d69736d6174636800604482015290519081900360640190fd5b6060885167ffffffffffffffff81118015612d1457600080fd5b50604051908082528060200260200182016040528015612d3e578160200160208202803683370190505b50905060005b8a518160ff161015612fbc5760ff811615612e2d5760d360008c8360ff1681518110612d6c57fe5b6020908102919091018101516001600160a01b031682528101919091526040016000205460ff16158015612ddc57508a8160ff1681518110612daa57fe5b60200260200101516001600160a01b03168b600081518110612dc857fe5b60200260200101516001600160a01b031614155b612e2d576040805162461bcd60e51b815260206004820152601060248201527f4475706c696361746520746f6b656e7300000000000000000000000000000000604482015290519081900360640190fd5b60006001600160a01b03168b8260ff1681518110612e4757fe5b60200260200101516001600160a01b03161415612eab576040805162461bcd60e51b815260206004820152601d60248201527f546865203020616464726573732069736e277420616e204552432d3230000000604482015290519081900360640190fd5b601260ff168a8260ff1681518110612ebf57fe5b602002602001015160ff161115612f1d576040805162461bcd60e51b815260206004820152601a60248201527f546f6b656e20646563696d616c732065786365656473206d6178000000000000604482015290519081900360640190fd5b612f4d8a8260ff1681518110612f2f57fe5b602002602001015160ff16601260ff1661374290919063ffffffff16565b600a0a828260ff1681518110612f5f57fe5b6020026020010181815250508060d360008d8460ff1681518110612f7f57fe5b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff191660ff92909216919091179055600101612d44565b50620f42408610613014576040805162461bcd60e51b815260206004820152601260248201527f5f612065786365656473206d6178696d756d0000000000000000000000000000604482015290519081900360640190fd5b6305f5e100851061306c576040805162461bcd60e51b815260206004820152601460248201527f5f6665652065786365656473206d6178696d756d000000000000000000000000604482015290519081900360640190fd5b6402540be40084106130c5576040805162461bcd60e51b815260206004820152601960248201527f5f61646d696e4665652065786365656473206d6178696d756d00000000000000604482015290519081900360640190fd5b60006130d08461379f565b9050806001600160a01b0316634cd88b768a8a6040518363ffffffff1660e01b8152600401808060200180602001838103835285818151815260200191508051906020019080838360005b8381101561313357818101518382015260200161311b565b50505050905090810190601f1680156131605780820380516001836020036101000a031916815260200191505b50838103825284518152845160209182019186019080838360005b8381101561319357818101518382015260200161317b565b50505050905090810190601f1680156131c05780820380516001836020036101000a031916815260200191505b50945050505050602060405180830381600087803b1580156131e157600080fd5b505af11580156131f5573d6000803e3d6000fd5b505050506040513d602081101561320b57600080fd5b505161325e576040805162461bcd60e51b815260206004820152601c60248201527f636f756c64206e6f7420696e6974206c70546f6b656e20636c6f6e6500000000604482015290519081900360640190fd5b60cf80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383161790558a516132a49060d09060208e0190613e9b565b5081516132b89060d1906020850190613f18565b508a5167ffffffffffffffff811180156132d157600080fd5b506040519080825280602002602001820160405280156132fb578160200160208202803683370190505b5080516133109160d291602090910190613f18565b5061331c87606461385a565b60c95561332a87606461385a565b60ca55505060cd84905560ce839055801561260b576000805461ff0019169055505050505050505050565b8015806133f45750604080517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b1580156133c657600080fd5b505afa1580156133da573d6000803e3d6000fd5b505050506040513d60208110156133f057600080fd5b5051155b61342f5760405162461bcd60e51b81526004018080602001828103825260368152602001806140b96036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b3000000000000000000000000000000000000000000000000000000001790526134af9084906138b3565b505050565b6134bc611ab9565b61350d576040805162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f7420706175736564000000000000000000000000604482015290519081900360640190fd5b6065805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa613540612b45565b604080516001600160a01b039092168252519081900360200190a1565b613565611ab9565b156135b7576040805162461bcd60e51b815260206004820152601060248201527f5061757361626c653a2070617573656400000000000000000000000000000000604482015290519081900360640190fd5b6065805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258613540612b45565b3b151590565b600054610100900460ff168061360c575061360c612b49565b8061361a575060005460ff16155b6136555760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff16158015613680576000805460ff1961ff0019909116610100171660011790555b613688613964565b613690613a04565b613698613b15565b80156136aa576000805461ff00191690555b50565b600054610100900460ff16806136c657506136c6612b49565b806136d4575060005460ff16155b61370f5760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff1615801561373a576000805460ff1961ff0019909116610100171660011790555b613698613bc0565b600082821115613799576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60006040517f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000081528260601b60148201527f5af43d82803e903d91602b57fd5bf3000000000000000000000000000000000060288201526037816000f09150506001600160a01b038116611b6b576040805162461bcd60e51b815260206004820152601660248201527f455243313136373a20637265617465206661696c656400000000000000000000604482015290519081900360640190fd5b600082613869575060006114b1565b8282028284828161387657fe5b0414611d035760405162461bcd60e51b815260040180806020018281038252602181526020018061404a6021913960400191505060405180910390fd5b6060613908826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613c669092919063ffffffff16565b8051909150156134af5780806020019051602081101561392757600080fd5b50516134af5760405162461bcd60e51b815260040180806020018281038252602a81526020018061408f602a913960400191505060405180910390fd5b600054610100900460ff168061397d575061397d612b49565b8061398b575060005460ff16155b6139c65760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff16158015613698576000805460ff1961ff00199091166101001716600117905580156136aa576000805461ff001916905550565b600054610100900460ff1680613a1d5750613a1d612b49565b80613a2b575060005460ff16155b613a665760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff16158015613a91576000805460ff1961ff0019909116610100171660011790555b6000613a9b612b45565b603380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a35080156136aa576000805461ff001916905550565b600054610100900460ff1680613b2e5750613b2e612b49565b80613b3c575060005460ff16155b613b775760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff16158015613ba2576000805460ff1961ff0019909116610100171660011790555b6065805460ff1916905580156136aa576000805461ff001916905550565b600054610100900460ff1680613bd95750613bd9612b49565b80613be7575060005460ff16155b613c225760405162461bcd60e51b815260040180806020018281038252602e815260200180613ff8602e913960400191505060405180910390fd5b600054610100900460ff16158015613c4d576000805460ff1961ff0019909116610100171660011790555b600160975580156136aa576000805461ff001916905550565b6060613c758484600085613c7d565b949350505050565b606082471015613cbe5760405162461bcd60e51b8152600401808060200182810382526026815260200180613fd26026913960400191505060405180910390fd5b613cc7856135ed565b613d18576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b60208310613d7557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101613d38565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114613dd7576040519150601f19603f3d011682016040523d82523d6000602084013e613ddc565b606091505b5091509150613dec828286613df7565b979650505050505050565b60608315613e06575081611d03565b825115613e165782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015613e60578181015183820152602001613e48565b50505050905090810190601f168015613e8d5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b828054828255906000526020600020908101928215613f08579160200282015b82811115613f0857825182547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03909116178255602090920191600190910190613ebb565b50613f14929150613f5f565b5090565b828054828255906000526020600020908101928215613f53579160200282015b82811115613f53578251825591602001919060010190613f38565b50613f14929150613f96565b5b80821115613f145780547fffffffffffffffffffffffff0000000000000000000000000000000000000000168155600101613f60565b5b80821115613f145760008155600101613f9756fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c496e697469616c697a61626c653a20636f6e747261637420697320616c726561647920696e697469616c697a6564626173654c50546f6b656e206973206e6f74206f776e6564206279206261736553776170536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f776261736553776170206d75737420706f6f6c206174206c65617374203220746f6b656e735361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e6365a2646970667358221220077a4a4d88a301ee87d30544652bdbf9fc343f9acdffcc5a0b8b2add767af86f64736f6c634300060c0033
Loading...
Loading
Loading...
Loading
Net Worth in USD
$174.32
Net Worth in ETH
0.059448
Token Allocations
SUSD
100.00%
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ETH | 100.00% | $0.783243 | 222.5657 | $174.32 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
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.