Transactions
Token Transfers
Tokens
Internal Transactions
Coin Balance History
Logs
Code
Read Contract
Write Contract
- Contract name:
- StableSwap
- Optimization enabled
- true
- Compiler version
- v0.8.4+commit.c7e474f2
- Optimization runs
- 200
- EVM Version
- default
- Verified at
- 2021-09-13 10:25:11.227652Z
Contract source code
// File @openzeppelin/contracts/security/ReentrancyGuard.sol@v4.3.1
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_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;
}
}
// File @openzeppelin/contracts/proxy/utils/Initializable.sol@v4.3.1
pragma solidity ^0.8.0;
/**
* @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 {ERC1967Proxy-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 || !_initialized, "Initializable: contract is already initialized");
bool isTopLevelCall = !_initializing;
if (isTopLevelCall) {
_initializing = true;
_initialized = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
}
}
}
// File @openzeppelin/contracts/token/ERC20/IERC20.sol@v4.3.1
pragma solidity ^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);
}
// File @openzeppelin/contracts/utils/Address.sol@v4.3.1
pragma solidity ^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;
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");
(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");
(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");
(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");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// 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
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
// File @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol@v4.3.1
pragma solidity ^0.8.0;
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
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'
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) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_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
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
// File @openzeppelin/contracts/utils/Context.sol@v4.3.1
pragma solidity ^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 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) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
// File @openzeppelin/contracts/access/Ownable.sol@v4.3.1
pragma solidity ^0.8.0;
/**
* @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 Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_setOwner(_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 {
_setOwner(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");
_setOwner(newOwner);
}
function _setOwner(address newOwner) private {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// File @openzeppelin/contracts/security/Pausable.sol@v4.3.1
pragma solidity ^0.8.0;
/**
* @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 Pausable is Context {
/**
* @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.
*/
constructor() {
_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());
}
}
// File contracts/stableswap/OwnerPausable.sol
pragma solidity 0.8.4;
abstract contract OwnerPausable is Ownable, Pausable {
function pause() external onlyOwner {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
}
// File @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol@v4.3.1
pragma solidity ^0.8.0;
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// File @openzeppelin/contracts/token/ERC20/ERC20.sol@v4.3.1
pragma solidity ^0.8.0;
/**
* @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 Contracts guidelines: functions revert
* instead 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, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override 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 this function is
* overridden;
*
* 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 override returns (uint8) {
return 18;
}
/**
* @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);
uint256 currentAllowance = _allowances[sender][_msgSender()];
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(sender, _msgSender(), currentAllowance - amount);
}
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] + 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) {
uint256 currentAllowance = _allowances[_msgSender()][spender];
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(_msgSender(), spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `sender` to `recipient`.
*
* This 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);
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[sender] = senderBalance - amount;
}
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
_afterTokenTransfer(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:
*
* - `account` 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 += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(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);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
_afterTokenTransfer(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 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 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 {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been 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 _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}
// File @openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol@v4.3.1
pragma solidity ^0.8.0;
/**
* @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 ERC20Burnable is Context, ERC20 {
/**
* @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 currentAllowance = allowance(account, _msgSender());
require(currentAllowance >= amount, "ERC20: burn amount exceeds allowance");
unchecked {
_approve(account, _msgSender(), currentAllowance - amount);
}
_burn(account, amount);
}
}
// File contracts/interfaces/IStableSwap.sol
pragma solidity 0.8.4;
interface IStableSwap {
/// EVENTS
event AddLiquidity(
address indexed provider,
uint256[] tokenAmounts,
uint256[] fees,
uint256 invariant,
uint256 tokenSupply
);
event TokenExchange(
address indexed buyer,
uint256 soldId,
uint256 tokensSold,
uint256 boughtId,
uint256 tokensBought
);
event RemoveLiquidity(address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 tokenSupply);
event RemoveLiquidityOne(address indexed provider, uint256 tokenIndex, uint256 tokenAmount, uint256 coinAmount);
event RemoveLiquidityImbalance(
address indexed provider,
uint256[] tokenAmounts,
uint256[] fees,
uint256 invariant,
uint256 tokenSupply
);
event RampA(uint256 oldA, uint256 newA, uint256 initialTime, uint256 futureTime);
event StopRampA(uint256 A, uint256 timestamp);
event NewFee(uint256 fee, uint256 adminFee);
event CollectProtocolFee(address token, uint256 amount);
event FeeControllerChanged(address newController);
event FeeDistributorChanged(address newController);
// pool data view functions
function getLpToken() external view returns (IERC20 lpToken);
function getA() external view returns (uint256);
function getAPrecise() external view returns (uint256);
function getToken(uint8 index) external view returns (IERC20);
function getTokens() external view returns (IERC20[] memory);
function getTokenIndex(address tokenAddress) external view returns (uint8);
function getTokenBalance(uint8 index) external view returns (uint256);
function getTokenBalances() external view returns (uint256[] memory);
function getNumberOfTokens() external view returns (uint256);
function getVirtualPrice() external view returns (uint256);
function calculateTokenAmount(uint256[] calldata amounts, bool deposit) external view returns (uint256);
function calculateSwap(
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx
) external view returns (uint256);
function calculateRemoveLiquidity(uint256 amount) external view returns (uint256[] memory);
function calculateRemoveLiquidityOneToken(uint256 tokenAmount, uint8 tokenIndex)
external
view
returns (uint256 availableTokenAmount);
function getAdminBalances() external view returns (uint256[] memory adminBalances);
function getAdminBalance(uint8 index) external view returns (uint256);
// state modifying functions
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);
function withdrawAdminFee() external;
}
// File contracts/stableswap/LPToken.sol
pragma solidity ^0.8.4;
contract LPToken is Ownable, ERC20Burnable {
IStableSwap public swap;
constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) {
swap = IStableSwap(msg.sender);
}
function mint(address _to, uint256 _amount) external onlyOwner {
require(_amount > 0, "zeroMintAmount");
_mint(_to, _amount);
}
}
// File contracts/stableswap/StableSwapStorage.sol
pragma solidity 0.8.4;
/**
* StableSwap main algorithm
*/
library StableSwapStorage {
using SafeERC20 for IERC20;
event AddLiquidity(
address indexed provider,
uint256[] token_amounts,
uint256[] fees,
uint256 invariant,
uint256 token_supply
);
event TokenExchange(
address indexed buyer,
uint256 sold_id,
uint256 tokens_sold,
uint256 bought_id,
uint256 tokens_bought
);
event RemoveLiquidity(address indexed provider, uint256[] token_amounts, uint256[] fees, uint256 token_supply);
event RemoveLiquidityOne(address indexed provider, uint256 index, uint256 token_amount, uint256 coin_amount);
event RemoveLiquidityImbalance(
address indexed provider,
uint256[] token_amounts,
uint256[] fees,
uint256 invariant,
uint256 token_supply
);
uint256 public constant FEE_DENOMINATOR = 1e10;
// uint256 public constant PRECISION = 1e18;
/// @dev protect from division loss when run approximation loop. We cannot divide at the end because of overflow,
/// so we add some (small) PRECISION when divide in each iteration
uint256 public constant A_PRECISION = 100;
/// @dev max iteration of converge calccuate
uint256 internal constant MAX_ITERATION = 256;
uint256 public constant POOL_TOKEN_COMMON_DECIMALS = 18;
struct SwapStorage {
IERC20[] pooledTokens;
LPToken lpToken;
/// @dev token i multiplier to reach POOL_TOKEN_COMMON_DECIMALS
uint256[] tokenMultipliers;
/// @dev effective balance which might different from token balance of the contract 'cause it hold admin fee as well
uint256[] balances;
/// @dev swap fee ratio. Charge on any action which move balance state far from the ideal state
uint256 fee;
/// @dev admin fee in ratio of swap fee.
uint256 adminFee;
/// @dev observation of A, multiplied with A_PRECISION
uint256 initialA;
uint256 futureA;
uint256 initialATime;
uint256 futureATime;
}
/**
* @notice Deposit coins into the pool
* @param amounts List of amounts of coins to deposit
* @param minMintAmount Minimum amount of LP tokens to mint from the deposit
* @return mintAmount Amount of LP tokens received by depositing
*/
function addLiquidity(
SwapStorage storage self,
uint256[] memory amounts,
uint256 minMintAmount
) external returns (uint256 mintAmount) {
uint256 nCoins = self.pooledTokens.length;
require(amounts.length == nCoins, "invalidAmountsLength");
uint256[] memory fees = new uint256[](nCoins);
uint256 _fee = _feePerToken(self);
uint256 tokenSupply = self.lpToken.totalSupply();
uint256 amp = _getAPrecise(self);
uint256 D0 = 0;
if (tokenSupply > 0) {
D0 = _getD(_xp(self.balances, self.tokenMultipliers), amp);
}
uint256[] memory newBalances = self.balances;
for (uint256 i = 0; i < nCoins; i++) {
if (tokenSupply == 0) {
require(amounts[i] > 0, "initialDepositRequireAllTokens");
}
// get real transfer in amount
newBalances[i] += _doTransferIn(self.pooledTokens[i], amounts[i]);
}
uint256 D1 = _getD(_xp(newBalances, self.tokenMultipliers), amp);
assert(D1 > D0); // double check
if (tokenSupply == 0) {
self.balances = newBalances;
mintAmount = D1;
} else {
uint256 diff = 0;
for (uint256 i = 0; i < nCoins; i++) {
diff = _distance((D1 * self.balances[i]) / D0, newBalances[i]);
fees[i] = (_fee * diff) / FEE_DENOMINATOR;
self.balances[i] = newBalances[i] - ((fees[i] * self.adminFee) / FEE_DENOMINATOR);
newBalances[i] -= fees[i];
}
D1 = _getD(_xp(newBalances, self.tokenMultipliers), amp);
mintAmount = (tokenSupply * (D1 - D0)) / D0;
}
require(mintAmount >= minMintAmount, "> slippage");
self.lpToken.mint(msg.sender, mintAmount);
emit AddLiquidity(msg.sender, amounts, fees, D1, mintAmount);
}
function swap(
SwapStorage storage self,
uint256 i,
uint256 j,
uint256 inAmount,
uint256 minOutAmount
) external returns (uint256) {
IERC20 inCoin = self.pooledTokens[i];
uint256[] memory normalizedBalances = _xp(self);
inAmount = _doTransferIn(inCoin, inAmount);
uint256 x = normalizedBalances[i] + (inAmount * self.tokenMultipliers[i]);
uint256 y = _getY(self, i, j, x, normalizedBalances);
uint256 dy = normalizedBalances[j] - y - 1; // iliminate rouding errors
uint256 dy_fee = (dy * self.fee) / FEE_DENOMINATOR;
dy = (dy - dy_fee) / self.tokenMultipliers[j]; // denormalize
require(dy >= minOutAmount, "> slippage");
uint256 _adminFee = (dy_fee * self.adminFee) / FEE_DENOMINATOR / self.tokenMultipliers[j];
// update balances
self.balances[i] += inAmount;
self.balances[j] -= dy + _adminFee;
self.pooledTokens[j].safeTransfer(msg.sender, dy);
emit TokenExchange(msg.sender, i, inAmount, j, dy);
return dy;
}
function removeLiquidity(
SwapStorage storage self,
uint256 lpAmount,
uint256[] memory minAmounts
) external returns (uint256[] memory amounts) {
uint256 totalSupply = self.lpToken.totalSupply();
require(lpAmount <= totalSupply);
uint256 nCoins = self.pooledTokens.length;
uint256[] memory fees = new uint256[](nCoins);
amounts = _calculateRemoveLiquidity(self, lpAmount);
for (uint256 i = 0; i < amounts.length; i++) {
require(amounts[i] >= minAmounts[i], "> slippage");
self.balances[i] = self.balances[i] - amounts[i];
self.pooledTokens[i].safeTransfer(msg.sender, amounts[i]);
}
self.lpToken.burnFrom(msg.sender, lpAmount);
emit RemoveLiquidity(msg.sender, amounts, fees, totalSupply - lpAmount);
}
function removeLiquidityOneToken(
SwapStorage storage self,
uint256 lpAmount,
uint256 index,
uint256 minAmount
) external returns (uint256) {
uint256 totalSupply = self.lpToken.totalSupply();
require(totalSupply > 0, "totalSupply = 0");
uint256 numTokens = self.pooledTokens.length;
require(lpAmount <= self.lpToken.balanceOf(msg.sender), "> balance");
require(lpAmount <= totalSupply, "> totalSupply");
require(index < numTokens, "tokenNotFound");
uint256 dyFee;
uint256 dy;
(dy, dyFee) = _calculateRemoveLiquidityOneToken(self, lpAmount, index);
require(dy >= minAmount, "> slippage");
self.balances[index] -= (dy + (dyFee * self.adminFee) / FEE_DENOMINATOR);
self.lpToken.burnFrom(msg.sender, lpAmount);
self.pooledTokens[index].safeTransfer(msg.sender, dy);
emit RemoveLiquidityOne(msg.sender, index, lpAmount, dy);
return dy;
}
function removeLiquidityImbalance(
SwapStorage storage self,
uint256[] memory amounts,
uint256 maxBurnAmount
) external returns (uint256 burnAmount) {
uint256 nCoins = self.pooledTokens.length;
require(amounts.length == nCoins, "invalidAmountsLength");
uint256 totalSupply = self.lpToken.totalSupply();
require(totalSupply != 0, "totalSupply = 0");
uint256 _fee = _feePerToken(self);
uint256 amp = _getAPrecise(self);
uint256[] memory newBalances = self.balances;
uint256 D0 = _getD(_xp(self), amp);
for (uint256 i = 0; i < nCoins; i++) {
newBalances[i] -= amounts[i];
}
uint256 D1 = _getD(_xp(newBalances, self.tokenMultipliers), amp);
uint256[] memory fees = new uint256[](nCoins);
for (uint256 i = 0; i < nCoins; i++) {
uint256 idealBalance = (D1 * self.balances[i]) / D0;
uint256 diff = _distance(newBalances[i], idealBalance);
fees[i] = (_fee * diff) / FEE_DENOMINATOR;
self.balances[i] = newBalances[i] - ((fees[i] * self.adminFee) / FEE_DENOMINATOR);
newBalances[i] -= fees[i];
}
// recalculate invariant with fee charged balances
D1 = _getD(_xp(newBalances, self.tokenMultipliers), amp);
burnAmount = ((D0 - D1) * totalSupply) / D0;
assert(burnAmount > 0);
require(burnAmount <= maxBurnAmount, "> slippage");
self.lpToken.burnFrom(msg.sender, burnAmount);
for (uint256 i = 0; i < nCoins; i++) {
if (amounts[i] != 0) {
self.pooledTokens[i].safeTransfer(msg.sender, amounts[i]);
}
}
emit RemoveLiquidityImbalance(msg.sender, amounts, fees, D1, totalSupply - burnAmount);
}
/// VIEW FUNCTIONS
function getAPrecise(SwapStorage storage self) external view returns (uint256) {
return _getAPrecise(self);
}
/**
* Returns portfolio virtual price (for calculating profit)
* scaled up by 1e18
*/
function getVirtualPrice(SwapStorage storage self) external view returns (uint256) {
uint256 D = _getD(_xp(self), _getAPrecise(self));
uint256 tokenSupply = self.lpToken.totalSupply();
return (D * 10**POOL_TOKEN_COMMON_DECIMALS) / tokenSupply;
}
function getAdminBalance(SwapStorage storage self, uint256 index) external view returns (uint256) {
require(index < self.pooledTokens.length, "indexOutOfRange");
return self.pooledTokens[index].balanceOf(address(this)) - (self.balances[index]);
}
/**
* Estimate amount of LP token minted or burned at deposit or withdrawal
* without taking fees into account
*/
function calculateTokenAmount(
SwapStorage storage self,
uint256[] memory amounts,
bool deposit
) external view returns (uint256) {
uint256 nCoins = self.pooledTokens.length;
require(amounts.length == nCoins, "invalidAmountsLength");
uint256 amp = _getAPrecise(self);
uint256 D0 = _getD(_xp(self), amp);
uint256[] memory newBalances = self.balances;
for (uint256 i = 0; i < nCoins; i++) {
if (deposit) {
newBalances[i] += amounts[i];
} else {
newBalances[i] -= amounts[i];
}
}
uint256 D1 = _getD(_xp(newBalances, self.tokenMultipliers), amp);
uint256 totalSupply = self.lpToken.totalSupply();
if (totalSupply == 0) {
return D1; // first depositor take it all
}
uint256 diff = deposit ? D1 - D0 : D0 - D1;
return (diff * self.lpToken.totalSupply()) / D0;
}
function getA(SwapStorage storage self) external view returns (uint256) {
return _getAPrecise(self) / A_PRECISION;
}
function calculateSwap(
SwapStorage storage self,
uint256 inIndex,
uint256 outIndex,
uint256 inAmount
) external view returns (uint256) {
uint256[] memory normalizedBalances = _xp(self);
uint256 newInBalance = normalizedBalances[inIndex] + (inAmount * self.tokenMultipliers[inIndex]);
uint256 outBalance = _getY(self, inIndex, outIndex, newInBalance, normalizedBalances);
uint256 outAmount = (normalizedBalances[outIndex] - outBalance - 1) / self.tokenMultipliers[outIndex];
uint256 _fee = (self.fee * outAmount) / FEE_DENOMINATOR;
return outAmount - _fee;
}
function calculateRemoveLiquidity(SwapStorage storage self, uint256 amount)
external
view
returns (uint256[] memory)
{
return _calculateRemoveLiquidity(self, amount);
}
function calculateRemoveLiquidityOneToken(
SwapStorage storage self,
uint256 lpAmount,
uint256 tokenIndex
) external view returns (uint256 amount) {
(amount, ) = _calculateRemoveLiquidityOneToken(self, lpAmount, tokenIndex);
}
/// INTERNAL FUNCTIONS
/**
* Ramping A up or down, return A with precision of A_PRECISION
*/
function _getAPrecise(SwapStorage storage self) internal view returns (uint256) {
if (block.timestamp >= self.futureATime) {
return self.futureA;
}
if (self.futureA > self.initialA) {
return
self.initialA +
((self.futureA - self.initialA) * (block.timestamp - self.initialATime)) /
(self.futureATime - self.initialATime);
}
return
self.initialA -
((self.initialA - self.futureA) * (block.timestamp - self.initialATime)) /
(self.futureATime - self.initialATime);
}
/**
* normalized balances of each tokens.
*/
function _xp(uint256[] memory balances, uint256[] memory rates) internal pure returns (uint256[] memory) {
for (uint256 i = 0; i < balances.length; i++) {
rates[i] = (rates[i] * balances[i]);
}
return rates;
}
function _xp(SwapStorage storage self) internal view returns (uint256[] memory) {
return _xp(self.balances, self.tokenMultipliers);
}
/**
* Calculate D for *NORMALIZED* balances of each tokens
* @param xp normalized balances of token
*/
function _getD(uint256[] memory xp, uint256 amp) internal pure returns (uint256) {
uint256 nCoins = xp.length;
uint256 sum = _sumOf(xp);
if (sum == 0) {
return 0;
}
uint256 Dprev = 0;
uint256 D = sum;
uint256 Ann = amp * nCoins;
for (uint256 i = 0; i < MAX_ITERATION; i++) {
uint256 D_P = D;
for (uint256 j = 0; j < xp.length; j++) {
D_P = (D_P * D) / (xp[j] * nCoins);
}
Dprev = D;
D =
(((Ann * sum) / A_PRECISION + D_P * nCoins) * D) /
(((Ann - A_PRECISION) * D) / A_PRECISION + (nCoins + 1) * D_P);
if (_distance(D, Dprev) <= 1) {
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("invariantCalculationFailed");
}
/**
* calculate new balance of when swap
* Done by solving quadratic equation iteratively.
* x_1**2 + x_1 * (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 inIndex index of token to swap in
* @param outIndex index of token to swap out
* @param inBalance new balance (normalized) of input token if the swap success
* @return NORMALIZED balance of output token if the swap success
*/
function _getY(
SwapStorage storage self,
uint256 inIndex,
uint256 outIndex,
uint256 inBalance,
uint256[] memory normalizedBalances
) internal view returns (uint256) {
require(inIndex != outIndex, "sameToken");
uint256 nCoins = self.pooledTokens.length;
require(inIndex < nCoins && outIndex < nCoins, "indexOutOfRange");
uint256 amp = _getAPrecise(self);
uint256 Ann = amp * nCoins;
uint256 D = _getD(normalizedBalances, amp);
uint256 sum = 0; // sum of new balances except output token
uint256 c = D;
for (uint256 i = 0; i < nCoins; i++) {
if (i == outIndex) {
continue;
}
uint256 x = i == inIndex ? inBalance : normalizedBalances[i];
sum += x;
c = (c * D) / (x * nCoins);
}
c = (c * D * A_PRECISION) / (Ann * nCoins);
uint256 b = sum + (D * A_PRECISION) / Ann;
uint256 lastY = 0;
uint256 y = D;
for (uint256 index = 0; index < MAX_ITERATION; index++) {
lastY = y;
y = (y * y + c) / (2 * y + b - D);
if (_distance(lastY, y) <= 1) {
return y;
}
}
revert("yCalculationFailed");
}
function _calculateRemoveLiquidity(SwapStorage storage self, uint256 amount)
internal
view
returns (uint256[] memory)
{
uint256 totalSupply = self.lpToken.totalSupply();
require(amount <= totalSupply, "Cannot exceed total supply");
uint256[] memory amounts = new uint256[](self.pooledTokens.length);
for (uint256 i = 0; i < self.pooledTokens.length; i++) {
amounts[i] = (self.balances[i] * (amount)) / (totalSupply);
}
return amounts;
}
function _calculateRemoveLiquidityOneToken(
SwapStorage storage self,
uint256 tokenAmount,
uint256 index
) internal view returns (uint256 dy, uint256 fee) {
require(index < self.pooledTokens.length, "indexOutOfRange");
uint256 amp = _getAPrecise(self);
uint256[] memory xp = _xp(self);
uint256 D0 = _getD(xp, amp);
uint256 D1 = D0 - (tokenAmount * D0) / self.lpToken.totalSupply();
uint256 newY = _getYD(self, amp, index, xp, D1);
uint256[] memory reducedXP = xp;
uint256 _fee = _feePerToken(self);
for (uint256 i = 0; i < self.pooledTokens.length; i++) {
uint256 expectedDx = 0;
if (i == index) {
expectedDx = (xp[i] * D1) / D0 - newY;
} else {
expectedDx = xp[i] - (xp[i] * D1) / D0;
}
reducedXP[i] -= (_fee * expectedDx) / FEE_DENOMINATOR;
}
dy = reducedXP[index] - _getYD(self, amp, index, reducedXP, D1);
dy = (dy - 1) / self.tokenMultipliers[index];
fee = ((xp[index] - newY) / self.tokenMultipliers[index]) - dy;
}
function _feePerToken(SwapStorage storage self) internal view returns (uint256) {
uint256 nCoins = self.pooledTokens.length;
return (self.fee * nCoins) / (4 * (nCoins - 1));
}
function _getYD(
SwapStorage storage self,
uint256 A,
uint256 index,
uint256[] memory xp,
uint256 D
) internal view returns (uint256) {
uint256 nCoins = self.pooledTokens.length;
assert(index < nCoins);
uint256 Ann = A * nCoins;
uint256 c = D;
uint256 s = 0;
uint256 _x = 0;
uint256 yPrev = 0;
for (uint256 i = 0; i < nCoins; i++) {
if (i == index) {
continue;
}
_x = xp[i];
s += _x;
c = (c * D) / (_x * nCoins);
}
c = (c * D * A_PRECISION) / (Ann * nCoins);
uint256 b = s + (D * A_PRECISION) / Ann;
uint256 y = D;
for (uint256 i = 0; i < MAX_ITERATION; i++) {
yPrev = y;
y = (y * y + c) / (2 * y + b - D);
if (_distance(yPrev, y) <= 1) {
return y;
}
}
revert("invariantCalculationFailed");
}
function _doTransferIn(IERC20 token, uint256 amount) internal returns (uint256) {
uint256 priorBalance = token.balanceOf(address(this));
token.safeTransferFrom(msg.sender, address(this), amount);
return token.balanceOf(address(this)) - priorBalance;
}
function _sumOf(uint256[] memory x) internal pure returns (uint256 sum) {
sum = 0;
for (uint256 i = 0; i < x.length; i++) {
sum += x[i];
}
}
function _distance(uint256 x, uint256 y) internal pure returns (uint256) {
return x > y ? x - y : y - x;
}
}
// File contracts/stableswap/StableSwap.sol
pragma solidity 0.8.4;
contract StableSwap is OwnerPausable, ReentrancyGuard, Initializable, IStableSwap {
using StableSwapStorage for StableSwapStorage.SwapStorage;
using SafeERC20 for IERC20;
/// constants
uint256 public constant MIN_RAMP_TIME = 1 days;
uint256 public constant MAX_A = 1e6; // max_a with precision
uint256 public constant MAX_A_CHANGE = 10;
uint256 public constant MAX_ADMIN_FEE = 1e10; // 100%
uint256 public constant MAX_SWAP_FEE = 1e8; // 1%
/// STATE VARS
StableSwapStorage.SwapStorage public swapStorage;
address public feeDistributor;
address public feeController;
mapping(address => uint8) public tokenIndexes;
modifier deadlineCheck(uint256 _deadline) {
require(block.timestamp <= _deadline, "timeout");
_;
}
modifier onlyFeeControllerOrOwner() {
require(msg.sender == feeController || msg.sender == owner(), "!feeControllerOrOwner");
_;
}
function initialize(
address[] memory _coins,
uint8[] memory _decimals,
string memory lpTokenName,
string memory lpTokenSymbol,
uint256 _A,
uint256 _fee,
uint256 _adminFee,
address _feeDistributor
) external onlyOwner initializer {
require(_coins.length == _decimals.length, "coinsLength != decimalsLength");
require(_feeDistributor != address(0), "feeDistributor = empty");
uint256 numberOfCoins = _coins.length;
uint256[] memory rates = new uint256[](numberOfCoins);
IERC20[] memory coins = new IERC20[](numberOfCoins);
for (uint256 i = 0; i < numberOfCoins; i++) {
require(_coins[i] != address(0), "invalidTokenAddress");
require(_decimals[i] <= StableSwapStorage.POOL_TOKEN_COMMON_DECIMALS, "invalidDecimals");
rates[i] = 10**(StableSwapStorage.POOL_TOKEN_COMMON_DECIMALS - _decimals[i]);
coins[i] = IERC20(_coins[i]);
tokenIndexes[address(coins[i])] = uint8(i);
}
require(_A < MAX_A, "> maxA");
require(_fee <= MAX_SWAP_FEE, "> maxSwapFee");
require(_adminFee <= MAX_ADMIN_FEE, "> maxAdminFee");
swapStorage.lpToken = new LPToken(lpTokenName, lpTokenSymbol);
swapStorage.balances = new uint256[](numberOfCoins);
swapStorage.tokenMultipliers = rates;
swapStorage.pooledTokens = coins;
swapStorage.initialA = _A * StableSwapStorage.A_PRECISION;
swapStorage.futureA = _A * StableSwapStorage.A_PRECISION;
swapStorage.fee = _fee;
swapStorage.adminFee = _adminFee;
feeDistributor = _feeDistributor;
}
/// PUBLIC FUNCTIONS
function addLiquidity(
uint256[] memory amounts,
uint256 minMintAmount,
uint256 deadline
) external override whenNotPaused nonReentrant deadlineCheck(deadline) returns (uint256) {
return swapStorage.addLiquidity(amounts, minMintAmount);
}
function swap(
uint8 fromIndex,
uint8 toIndex,
uint256 inAmount,
uint256 minOutAmount,
uint256 deadline
) external override whenNotPaused nonReentrant deadlineCheck(deadline) returns (uint256) {
return swapStorage.swap(fromIndex, toIndex, inAmount, minOutAmount);
}
function removeLiquidity(
uint256 lpAmount,
uint256[] memory minAmounts,
uint256 deadline
) external override nonReentrant deadlineCheck(deadline) returns (uint256[] memory) {
return swapStorage.removeLiquidity(lpAmount, minAmounts);
}
function removeLiquidityOneToken(
uint256 lpAmount,
uint8 index,
uint256 minAmount,
uint256 deadline
) external override nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) {
return swapStorage.removeLiquidityOneToken(lpAmount, index, minAmount);
}
function removeLiquidityImbalance(
uint256[] memory amounts,
uint256 maxBurnAmount,
uint256 deadline
) external override nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) {
return swapStorage.removeLiquidityImbalance(amounts, maxBurnAmount);
}
/// VIEW FUNCTIONS
function getVirtualPrice() external view override returns (uint256) {
return swapStorage.getVirtualPrice();
}
function getA() external view override returns (uint256) {
return swapStorage.getA();
}
function getAPrecise() external view override returns (uint256) {
return swapStorage.getAPrecise();
}
function getTokens() external view override returns (IERC20[] memory) {
return swapStorage.pooledTokens;
}
function getToken(uint8 index) external view override returns (IERC20) {
return swapStorage.pooledTokens[index];
}
function getLpToken() external view override returns (IERC20) {
return swapStorage.lpToken;
}
function getTokenIndex(address token) external view override returns (uint8 index) {
index = tokenIndexes[token];
require(address(swapStorage.pooledTokens[index]) == token, "tokenNotFound");
}
function getTokenPrecisionMultipliers() external view returns (uint256[] memory) {
return swapStorage.tokenMultipliers;
}
function getTokenBalances() external view override returns (uint256[] memory) {
return swapStorage.balances;
}
function getTokenBalance(uint8 index) external view override returns (uint256) {
return swapStorage.balances[index];
}
function getNumberOfTokens() external view override returns (uint256) {
return swapStorage.pooledTokens.length;
}
function getAdminBalances() external view override returns (uint256[] memory adminBalances) {
uint256 length = swapStorage.pooledTokens.length;
adminBalances = new uint256[](length);
for (uint256 i = 0; i < length; i++) {
adminBalances[i] = swapStorage.getAdminBalance(i);
}
}
function getAdminBalance(uint8 index) external view override returns (uint256) {
return swapStorage.getAdminBalance((index));
}
function calculateTokenAmount(uint256[] calldata amounts, bool deposit) external view override returns (uint256) {
return swapStorage.calculateTokenAmount(amounts, deposit);
}
function calculateSwap(
uint8 inIndex,
uint8 outIndex,
uint256 inAmount
) external view override returns (uint256) {
return swapStorage.calculateSwap(inIndex, outIndex, inAmount);
}
function calculateRemoveLiquidity(uint256 amount) external view override returns (uint256[] memory) {
return swapStorage.calculateRemoveLiquidity(amount);
}
function calculateRemoveLiquidityOneToken(uint256 amount, uint8 index) external view override returns (uint256) {
return swapStorage.calculateRemoveLiquidityOneToken(amount, index);
}
/// RESTRICTED FUNCTION
/**
* @notice Sets the admin fee
* @dev adminFee cannot be higher than 100% of the swap fee
* swap fee cannot be higher than 1% of each swap
* @param newSwapFee new swap fee to be applied on future transactions
* @param newAdminFee new admin fee to be applied on future transactions
*/
function setFee(uint256 newSwapFee, uint256 newAdminFee) external onlyOwner {
require(newSwapFee <= MAX_SWAP_FEE, "> maxSwapFee");
require(newAdminFee <= MAX_ADMIN_FEE, "> maxAdminFee");
swapStorage.adminFee = newAdminFee;
swapStorage.fee = newSwapFee;
emit NewFee(newSwapFee, newAdminFee);
}
/**
* @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 futureATime timestamp when the new A should be reached
*/
function rampA(uint256 futureA, uint256 futureATime) external onlyOwner {
require(block.timestamp >= swapStorage.initialATime + (1 days), "< rampDelay"); // please wait 1 days before start a new ramping
require(futureATime >= block.timestamp + (MIN_RAMP_TIME), "< minRampTime");
require(0 < futureA && futureA < MAX_A, "outOfRange");
uint256 initialAPrecise = swapStorage.getAPrecise();
uint256 futureAPrecise = futureA * StableSwapStorage.A_PRECISION;
if (futureAPrecise < initialAPrecise) {
require(futureAPrecise * (MAX_A_CHANGE) >= initialAPrecise, "> maxChange");
} else {
require(futureAPrecise <= initialAPrecise * (MAX_A_CHANGE), "> maxChange");
}
swapStorage.initialA = initialAPrecise;
swapStorage.futureA = futureAPrecise;
swapStorage.initialATime = block.timestamp;
swapStorage.futureATime = futureATime;
emit RampA(initialAPrecise, futureAPrecise, block.timestamp, futureATime);
}
function stopRampA() external onlyOwner {
require(swapStorage.futureATime > block.timestamp, "alreadyStopped");
uint256 currentA = swapStorage.getAPrecise();
swapStorage.initialA = currentA;
swapStorage.futureA = currentA;
swapStorage.initialATime = block.timestamp;
swapStorage.futureATime = block.timestamp;
emit StopRampA(currentA, block.timestamp);
}
function setFeeController(address _feeController) external onlyOwner {
require(_feeController != address(0), "zeroAddress");
feeController = _feeController;
emit FeeControllerChanged(_feeController);
}
function setFeeDistributor(address _feeDistributor) external onlyOwner {
require(_feeDistributor != address(0), "zeroAddress");
feeDistributor = _feeDistributor;
emit FeeDistributorChanged(_feeDistributor);
}
function withdrawAdminFee() external override onlyFeeControllerOrOwner {
for (uint256 i = 0; i < swapStorage.pooledTokens.length; i++) {
IERC20 token = swapStorage.pooledTokens[i];
uint256 balance = token.balanceOf(address(this)) - (swapStorage.balances[i]);
if (balance != 0) {
token.safeTransfer(feeDistributor, balance);
emit CollectProtocolFee(address(token), balance);
}
}
}
}
Contract ABI
[{"type":"event","name":"AddLiquidity","inputs":[{"type":"address","name":"provider","internalType":"address","indexed":true},{"type":"uint256[]","name":"tokenAmounts","internalType":"uint256[]","indexed":false},{"type":"uint256[]","name":"fees","internalType":"uint256[]","indexed":false},{"type":"uint256","name":"invariant","internalType":"uint256","indexed":false},{"type":"uint256","name":"tokenSupply","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"CollectProtocolFee","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"FeeControllerChanged","inputs":[{"type":"address","name":"newController","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"FeeDistributorChanged","inputs":[{"type":"address","name":"newController","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"NewFee","inputs":[{"type":"uint256","name":"fee","internalType":"uint256","indexed":false},{"type":"uint256","name":"adminFee","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Paused","inputs":[{"type":"address","name":"account","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"RampA","inputs":[{"type":"uint256","name":"oldA","internalType":"uint256","indexed":false},{"type":"uint256","name":"newA","internalType":"uint256","indexed":false},{"type":"uint256","name":"initialTime","internalType":"uint256","indexed":false},{"type":"uint256","name":"futureTime","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RemoveLiquidity","inputs":[{"type":"address","name":"provider","internalType":"address","indexed":true},{"type":"uint256[]","name":"tokenAmounts","internalType":"uint256[]","indexed":false},{"type":"uint256[]","name":"fees","internalType":"uint256[]","indexed":false},{"type":"uint256","name":"tokenSupply","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RemoveLiquidityImbalance","inputs":[{"type":"address","name":"provider","internalType":"address","indexed":true},{"type":"uint256[]","name":"tokenAmounts","internalType":"uint256[]","indexed":false},{"type":"uint256[]","name":"fees","internalType":"uint256[]","indexed":false},{"type":"uint256","name":"invariant","internalType":"uint256","indexed":false},{"type":"uint256","name":"tokenSupply","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RemoveLiquidityOne","inputs":[{"type":"address","name":"provider","internalType":"address","indexed":true},{"type":"uint256","name":"tokenIndex","internalType":"uint256","indexed":false},{"type":"uint256","name":"tokenAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"coinAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"StopRampA","inputs":[{"type":"uint256","name":"A","internalType":"uint256","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"TokenExchange","inputs":[{"type":"address","name":"buyer","internalType":"address","indexed":true},{"type":"uint256","name":"soldId","internalType":"uint256","indexed":false},{"type":"uint256","name":"tokensSold","internalType":"uint256","indexed":false},{"type":"uint256","name":"boughtId","internalType":"uint256","indexed":false},{"type":"uint256","name":"tokensBought","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Unpaused","inputs":[{"type":"address","name":"account","internalType":"address","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_A","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_ADMIN_FEE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_A_CHANGE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_SWAP_FEE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MIN_RAMP_TIME","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"addLiquidity","inputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"},{"type":"uint256","name":"minMintAmount","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"calculateRemoveLiquidity","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"calculateRemoveLiquidityOneToken","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint8","name":"index","internalType":"uint8"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"calculateSwap","inputs":[{"type":"uint8","name":"inIndex","internalType":"uint8"},{"type":"uint8","name":"outIndex","internalType":"uint8"},{"type":"uint256","name":"inAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"calculateTokenAmount","inputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"},{"type":"bool","name":"deposit","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"feeController","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"feeDistributor","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getA","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getAPrecise","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getAdminBalance","inputs":[{"type":"uint8","name":"index","internalType":"uint8"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"adminBalances","internalType":"uint256[]"}],"name":"getAdminBalances","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"getLpToken","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getNumberOfTokens","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"getToken","inputs":[{"type":"uint8","name":"index","internalType":"uint8"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getTokenBalance","inputs":[{"type":"uint8","name":"index","internalType":"uint8"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"getTokenBalances","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"index","internalType":"uint8"}],"name":"getTokenIndex","inputs":[{"type":"address","name":"token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"getTokenPrecisionMultipliers","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"contract IERC20[]"}],"name":"getTokens","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getVirtualPrice","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address[]","name":"_coins","internalType":"address[]"},{"type":"uint8[]","name":"_decimals","internalType":"uint8[]"},{"type":"string","name":"lpTokenName","internalType":"string"},{"type":"string","name":"lpTokenSymbol","internalType":"string"},{"type":"uint256","name":"_A","internalType":"uint256"},{"type":"uint256","name":"_fee","internalType":"uint256"},{"type":"uint256","name":"_adminFee","internalType":"uint256"},{"type":"address","name":"_feeDistributor","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"pause","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"paused","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"rampA","inputs":[{"type":"uint256","name":"futureA","internalType":"uint256"},{"type":"uint256","name":"futureATime","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"removeLiquidity","inputs":[{"type":"uint256","name":"lpAmount","internalType":"uint256"},{"type":"uint256[]","name":"minAmounts","internalType":"uint256[]"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"removeLiquidityImbalance","inputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"},{"type":"uint256","name":"maxBurnAmount","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"removeLiquidityOneToken","inputs":[{"type":"uint256","name":"lpAmount","internalType":"uint256"},{"type":"uint8","name":"index","internalType":"uint8"},{"type":"uint256","name":"minAmount","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFee","inputs":[{"type":"uint256","name":"newSwapFee","internalType":"uint256"},{"type":"uint256","name":"newAdminFee","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFeeController","inputs":[{"type":"address","name":"_feeController","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFeeDistributor","inputs":[{"type":"address","name":"_feeDistributor","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"stopRampA","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"swap","inputs":[{"type":"uint8","name":"fromIndex","internalType":"uint8"},{"type":"uint8","name":"toIndex","internalType":"uint8"},{"type":"uint256","name":"inAmount","internalType":"uint256"},{"type":"uint256","name":"minOutAmount","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"lpToken","internalType":"contract LPToken"},{"type":"uint256","name":"fee","internalType":"uint256"},{"type":"uint256","name":"adminFee","internalType":"uint256"},{"type":"uint256","name":"initialA","internalType":"uint256"},{"type":"uint256","name":"futureA","internalType":"uint256"},{"type":"uint256","name":"initialATime","internalType":"uint256"},{"type":"uint256","name":"futureATime","internalType":"uint256"}],"name":"swapStorage","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"tokenIndexes","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"unpause","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdrawAdminFee","inputs":[]}]
Deployed ByteCode
0x60806040523480156200001157600080fd5b5060043610620002a95760003560e01c806384cdd9bc116200016d578063c4db7fa011620000d3578063e6ab28061162000092578063e6ab28061462000612578063efeecb511462000629578063f2fad2b61462000632578063f2fde38b1462000649578063f3de03621462000660578063fe49abe3146200066d57600080fd5b8063c4db7fa014620005d3578063ccfc2e8d14620005dd578063d41f656814620005f4578063d46300fd14620005fe578063e25aa5fa146200060857600080fd5b8063a1dc9031116200012c578063a1dc9031146200056d578063a95b089f1462000577578063aa6ca808146200058e578063ab3d854414620005a7578063ab5ac06114620005b3578063b28cb6dc14620005bc57600080fd5b806384cdd9bc14620004ff5780638554a7d414620005165780638da5cb5b146200052d57806391695586146200053f57806391ceb3eb146200055657600080fd5b80634d49e87d116200021357806366c0bd2411620001d257806366c0bd2414620004975780636999b37714620004ae578063715018a614620004c25780638214f5a414620004cc57806382b8660014620004de5780638456cb5914620004f557600080fd5b80634d49e87d14620003c857806352f7c98814620003df578063593d132c14620003f65780635c975abb146200040d5780635fd65f0f146200042b57600080fd5b806331cd52b0116200026c57806331cd52b01462000355578063342a87a1146200036c5780633969841514620003835780633e3a1560146200038e5780633ed4c67814620003a55780633f4ba83a14620003be57600080fd5b806304bc3b1c14620002ae57806306e9481c14620002eb5780630ba8195914620003055780630d43e8ad146200030f57806318f52ce2146200033c575b600080fd5b620002d4620002bf3660046200299c565b600f6020526000908152604090205460ff1681565b60405160ff90911681526020015b60405180910390f35b620002f66201518081565b604051908152602001620002e2565b620002f662000677565b600d5462000323906001600160a01b031681565b6040516001600160a01b039091168152602001620002e2565b6200034662000707565b604051620002e2919062002ec7565b620003466200036636600462002c63565b62000842565b620002f66200037d36600462002cd7565b6200093f565b620002f6620f424081565b620002f66200039f36600462002d05565b620009e2565b620003bc620003b63660046200299c565b62000b0c565b005b620003bc62000bd4565b620002f6620003d936600462002bc2565b62000c0d565b620003bc620003f036600462002cb5565b62000d1c565b620003bc6200040736600462002cb5565b62000e1c565b600054600160a01b900460ff166040519015158152602001620002e2565b600454600754600854600954600a54600b54600c5462000457966001600160a01b031695949392919087565b604080516001600160a01b0390981688526020880196909652948601939093526060850191909152608084015260a083015260c082015260e001620002e2565b620002d4620004a83660046200299c565b620010de565b600e5462000323906001600160a01b031681565b620003bc6200117a565b6004546001600160a01b031662000323565b62000323620004ef36600462002d42565b620011b3565b620003bc620011f8565b620002f66200051036600462002bc2565b6200122f565b620002f66200052736600462002d42565b620012eb565b6000546001600160a01b031662000323565b620002f66200055036600462002d9f565b6200137e565b620002f66200056736600462002d42565b620014b0565b62000346620014eb565b620002f66200058836600462002d5f565b62001547565b62000598620015f4565b604051620002e2919062002e78565b620002f66305f5e10081565b620002f6600a81565b620003bc620005cd366004620029b9565b6200165a565b620003bc62001cf7565b620003bc620005ee3660046200299c565b62001e42565b6200034662001f04565b620002f662001f5f565b620002f662001f9b565b620002f66200062336600462002a9e565b62001fd7565b600354620002f6565b620003466200064336600462002c31565b6200201a565b620003bc6200065a3660046200299c565b620020af565b620002f66402540be40081565b620003bc62002151565b604051637461cf1b60e01b815260036004820152600090733bccb81261aba479c1ecab28aa92a248ae2e130e90637461cf1b906024015b60206040518083038186803b158015620006c757600080fd5b505af4158015620006dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000702919062002c4a565b905090565b6003546060908067ffffffffffffffff8111156200073557634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156200075f578160200160208202803683370190505b50915060005b818110156200083d57604051630a1368cd60e01b81526003600482015260248101829052733bccb81261aba479c1ecab28aa92a248ae2e130e90630a1368cd9060440160206040518083038186803b158015620007c157600080fd5b505af4158015620007d6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620007fc919062002c4a565b8382815181106200081d57634e487b7160e01b600052603260045260246000fd5b60209081029190910101528062000834816200327f565b91505062000765565b505090565b606060026001541415620008735760405162461bcd60e51b81526004016200086a9062002fc8565b60405180910390fd5b600260015581428110156200089c5760405162461bcd60e51b81526004016200086a9062002fa7565b60405163d0b1a00560e01b8152733bccb81261aba479c1ecab28aa92a248ae2e130e9063d0b1a00590620008da906003908990899060040162003074565b60006040518083038186803b158015620008f357600080fd5b505af415801562000908573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000932919081019062002b26565b6001805595945050505050565b6040516309bba52360e31b8152600360048201526024810183905260ff82166044820152600090733bccb81261aba479c1ecab28aa92a248ae2e130e90634ddd29189060640160206040518083038186803b1580156200099e57600080fd5b505af4158015620009b3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620009d9919062002c4a565b90505b92915050565b60006002600154141562000a0a5760405162461bcd60e51b81526004016200086a9062002fc8565b6002600155600054600160a01b900460ff161562000a3c5760405162461bcd60e51b81526004016200086a9062002f48565b818042111562000a605760405162461bcd60e51b81526004016200086a9062002fa7565b6040516346732ec760e11b8152600360048201526024810187905260ff8616604482015260648101859052733bccb81261aba479c1ecab28aa92a248ae2e130e90638ce65d8e9060840160206040518083038186803b15801562000ac357600080fd5b505af415801562000ad8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000afe919062002c4a565b600180559695505050505050565b6000546001600160a01b0316331462000b395760405162461bcd60e51b81526004016200086a9062002f72565b6001600160a01b03811662000b7f5760405162461bcd60e51b815260206004820152600b60248201526a7a65726f4164647265737360a81b60448201526064016200086a565b600e80546001600160a01b0319166001600160a01b0383169081179091556040519081527f4c3f3b9852ccceadd50f16518f348e2624c8f0240acdd5bc81911c0fba83ec67906020015b60405180910390a150565b6000546001600160a01b0316331462000c015760405162461bcd60e51b81526004016200086a9062002f72565b62000c0b6200233f565b565b60008054600160a01b900460ff161562000c3b5760405162461bcd60e51b81526004016200086a9062002f48565b6002600154141562000c615760405162461bcd60e51b81526004016200086a9062002fc8565b6002600155814281101562000c8a5760405162461bcd60e51b81526004016200086a9062002fa7565b604051637217bafd60e11b8152733bccb81261aba479c1ecab28aa92a248ae2e130e9063e42f75fa9062000cc8906003908990899060040162003049565b60206040518083038186803b15801562000ce157600080fd5b505af415801562000cf6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000932919062002c4a565b6000546001600160a01b0316331462000d495760405162461bcd60e51b81526004016200086a9062002f72565b6305f5e10082111562000d8e5760405162461bcd60e51b815260206004820152600c60248201526b3e206d61785377617046656560a01b60448201526064016200086a565b6402540be40081111562000dd55760405162461bcd60e51b815260206004820152600d60248201526c3e206d617841646d696e46656560981b60448201526064016200086a565b6008819055600782905560408051838152602081018390527fbe12859b636aed607d5230b2cc2711f68d70e51060e6cca1f575ef5d2fcc95d1910160405180910390a15050565b6000546001600160a01b0316331462000e495760405162461bcd60e51b81526004016200086a9062002f72565b600b5462000e5b9062015180620030f0565b42101562000e9a5760405162461bcd60e51b815260206004820152600b60248201526a3c2072616d7044656c617960a81b60448201526064016200086a565b62000ea96201518042620030f0565b81101562000eea5760405162461bcd60e51b815260206004820152600d60248201526c3c206d696e52616d7054696d6560981b60448201526064016200086a565b81600010801562000efd5750620f424082105b62000f385760405162461bcd60e51b815260206004820152600a6024820152696f75744f6652616e676560b01b60448201526064016200086a565b604051637461cf1b60e01b815260036004820152600090733bccb81261aba479c1ecab28aa92a248ae2e130e90637461cf1b9060240160206040518083038186803b15801562000f8757600080fd5b505af415801562000f9c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000fc2919062002c4a565b9050600062000fd360648562003210565b90508181101562001030578162000fec600a8362003210565b10156200102a5760405162461bcd60e51b815260206004820152600b60248201526a3e206d61784368616e676560a81b60448201526064016200086a565b6200107c565b6200103d600a8362003210565b8111156200107c5760405162461bcd60e51b815260206004820152600b60248201526a3e206d61784368616e676560a81b60448201526064016200086a565b6009829055600a81905542600b819055600c84905560408051848152602081018490528082019290925260608201859052517fa2b71ec6df949300b59aab36b55e189697b750119dd349fcfa8c0f779e83c2549181900360800190a150505050565b6001600160a01b0381166000818152600f60205260409020546003805460ff9092169291839081106200112157634e487b7160e01b600052603260045260246000fd5b6000918252602090912001546001600160a01b031614620011755760405162461bcd60e51b815260206004820152600d60248201526c1d1bdad95b939bdd119bdd5b99609a1b60448201526064016200086a565b919050565b6000546001600160a01b03163314620011a75760405162461bcd60e51b81526004016200086a9062002f72565b62000c0b6000620023de565b600060036000018260ff1681548110620011dd57634e487b7160e01b600052603260045260246000fd5b6000918252602090912001546001600160a01b031692915050565b6000546001600160a01b03163314620012255760405162461bcd60e51b81526004016200086a9062002f72565b62000c0b6200242e565b600060026001541415620012575760405162461bcd60e51b81526004016200086a9062002fc8565b6002600155600054600160a01b900460ff1615620012895760405162461bcd60e51b81526004016200086a9062002f48565b8180421115620012ad5760405162461bcd60e51b81526004016200086a9062002fa7565b60405163576b973560e01b8152733bccb81261aba479c1ecab28aa92a248ae2e130e9063576b97359062000cc8906003908990899060040162003049565b604051630a1368cd60e01b81526003600482015260ff82166024820152600090733bccb81261aba479c1ecab28aa92a248ae2e130e90630a1368cd9060440160206040518083038186803b1580156200134357600080fd5b505af415801562001358573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620009dc919062002c4a565b60008054600160a01b900460ff1615620013ac5760405162461bcd60e51b81526004016200086a9062002f48565b60026001541415620013d25760405162461bcd60e51b81526004016200086a9062002fc8565b60026001558142811015620013fb5760405162461bcd60e51b81526004016200086a9062002fa7565b604051633a8d10bd60e01b81526003600482015260ff8089166024830152871660448201526064810186905260848101859052733bccb81261aba479c1ecab28aa92a248ae2e130e90633a8d10bd9060a40160206040518083038186803b1580156200146657600080fd5b505af41580156200147b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620014a1919062002c4a565b60018055979650505050505050565b6000600380018260ff1681548110620014d957634e487b7160e01b600052603260045260246000fd5b90600052602060002001549050919050565b6060600380018054806020026020016040519081016040528092919081815260200182805480156200153d57602002820191906000526020600020905b81548152602001906001019080831162001528575b5050505050905090565b6040516376311d0f60e11b81526003600482015260ff80851660248301528316604482015260648101829052600090733bccb81261aba479c1ecab28aa92a248ae2e130e9063ec623a1e906084015b60206040518083038186803b158015620015af57600080fd5b505af4158015620015c4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620015ea919062002c4a565b90505b9392505050565b606060036000018054806020026020016040519081016040528092919081815260200182805480156200153d57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162001632575050505050905090565b6000546001600160a01b03163314620016875760405162461bcd60e51b81526004016200086a9062002f72565b600254610100900460ff1680620016a1575060025460ff16155b620017065760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016200086a565b600254610100900460ff1615801562001729576002805461ffff19166101011790555b87518951146200177c5760405162461bcd60e51b815260206004820152601d60248201527f636f696e734c656e67746820213d20646563696d616c734c656e67746800000060448201526064016200086a565b6001600160a01b038216620017cd5760405162461bcd60e51b81526020600482015260166024820152756665654469737472696275746f72203d20656d70747960501b60448201526064016200086a565b885160008167ffffffffffffffff811115620017f957634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801562001823578160200160208202803683370190505b50905060008267ffffffffffffffff8111156200185057634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156200187a578160200160208202803683370190505b50905060005b8381101562001ad65760006001600160a01b03168d8281518110620018b557634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b031614156200190c5760405162461bcd60e51b8152602060048201526013602482015272696e76616c6964546f6b656e4164647265737360681b60448201526064016200086a565b60128c82815181106200192f57634e487b7160e01b600052603260045260246000fd5b602002602001015160ff1611156200197c5760405162461bcd60e51b815260206004820152600f60248201526e696e76616c6964446563696d616c7360881b60448201526064016200086a565b8b81815181106200199d57634e487b7160e01b600052603260045260246000fd5b602002602001015160ff166012620019b6919062003232565b620019c390600a62003154565b838281518110620019e457634e487b7160e01b600052603260045260246000fd5b6020026020010181815250508c818151811062001a1157634e487b7160e01b600052603260045260246000fd5b602002602001015182828151811062001a3a57634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160a01b031690816001600160a01b03168152505080600f600084848151811062001a8057634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff021916908360ff160217905550808062001acd906200327f565b91505062001880565b50620f4240881062001b145760405162461bcd60e51b81526020600482015260066024820152653e206d61784160d01b60448201526064016200086a565b6305f5e10087111562001b595760405162461bcd60e51b815260206004820152600c60248201526b3e206d61785377617046656560a01b60448201526064016200086a565b6402540be40086111562001ba05760405162461bcd60e51b815260206004820152600d60248201526c3e206d617841646d696e46656560981b60448201526064016200086a565b898960405162001bb090620026e2565b62001bbd92919062002f16565b604051809103906000f08015801562001bda573d6000803e3d6000fd5b50600480546001600160a01b0319166001600160a01b03929092169190911790558267ffffffffffffffff81111562001c2357634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801562001c4d578160200160208202803683370190505b50805162001c6491600691602090910190620026f0565b50815162001c7a906005906020850190620026f0565b50805162001c9090600390602084019062002740565b5062001c9e60648962003210565b60095562001cae60648962003210565b600a5550505060078490556008839055600d80546001600160a01b0319166001600160a01b038416179055801562001cec576002805461ff00191690555b505050505050505050565b6000546001600160a01b0316331462001d245760405162461bcd60e51b81526004016200086a9062002f72565b600c54421062001d685760405162461bcd60e51b815260206004820152600e60248201526d185b1c9958591e54dd1bdc1c195960921b60448201526064016200086a565b604051637461cf1b60e01b815260036004820152600090733bccb81261aba479c1ecab28aa92a248ae2e130e90637461cf1b9060240160206040518083038186803b15801562001db757600080fd5b505af415801562001dcc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001df2919062002c4a565b6009819055600a81905542600b819055600c8190556040519192507f46e22fb3709ad289f62ce63d469248536dbc78d82b84a3d7e74ad606dc2019389162000bc991848252602082015260400190565b6000546001600160a01b0316331462001e6f5760405162461bcd60e51b81526004016200086a9062002f72565b6001600160a01b03811662001eb55760405162461bcd60e51b815260206004820152600b60248201526a7a65726f4164647265737360a81b60448201526064016200086a565b600d80546001600160a01b0319166001600160a01b0383169081179091556040519081527fae5a12c29e496b092467a620746b9eaf4e0e231a631a4370c233b1fac38e8e269060200162000bc9565b606060036002018054806020026020016040519081016040528092919081815260200182805480156200153d576020028201919060005260206000209081548152602001906001019080831162001528575050505050905090565b60405163172a21f960e31b815260036004820152600090733bccb81261aba479c1ecab28aa92a248ae2e130e9063b9510fc890602401620006ae565b604051631bd62df560e11b815260036004820152600090733bccb81261aba479c1ecab28aa92a248ae2e130e906337ac5bea90602401620006ae565b6040516345b69ab760e01b8152600090733bccb81261aba479c1ecab28aa92a248ae2e130e906345b69ab790620015969060039088908890889060040162002fff565b60405163f94eed8760e01b81526003600482015260248101829052606090733bccb81261aba479c1ecab28aa92a248ae2e130e9063f94eed879060440160006040518083038186803b1580156200207057600080fd5b505af415801562002085573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620009dc919081019062002b26565b6000546001600160a01b03163314620020dc5760405162461bcd60e51b81526004016200086a9062002f72565b6001600160a01b038116620021435760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016200086a565b6200214e81620023de565b50565b600e546001600160a01b03163314806200217557506000546001600160a01b031633145b620021bb5760405162461bcd60e51b815260206004820152601560248201527410b332b2a1b7b73a3937b63632b927b927bbb732b960591b60448201526064016200086a565b60005b6003548110156200214e57600060036000018281548110620021f057634e487b7160e01b600052603260045260246000fd5b6000918252602082200154600680546001600160a01b03909216935090849081106200222c57634e487b7160e01b600052603260045260246000fd5b6000918252602090912001546040516370a0823160e01b81523060048201526001600160a01b038416906370a082319060240160206040518083038186803b1580156200227857600080fd5b505afa1580156200228d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620022b3919062002c4a565b620022bf919062003232565b905080156200232757600d54620022e4906001600160a01b0384811691168362002497565b604080516001600160a01b0384168152602081018390527fee3859efa95e525bc2bcb149b51b60a8bb4e89c647392d9d4112e03c3e73bdd6910160405180910390a15b5050808062002336906200327f565b915050620021be565b600054600160a01b900460ff16620023915760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b60448201526064016200086a565b6000805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600054600160a01b900460ff16156200245b5760405162461bcd60e51b81526004016200086a9062002f48565b6000805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258620023c13390565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052620024eb908490620024f0565b505050565b600062002547826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316620025c99092919063ffffffff16565b805190915015620024eb578080602001905181019062002568919062002c12565b620024eb5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016200086a565b6060620015ea848460008585843b620026255760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016200086a565b600080866001600160a01b0316858760405162002643919062002e5a565b60006040518083038185875af1925050503d806000811462002682576040519150601f19603f3d011682016040523d82523d6000602084013e62002687565b606091505b509150915062002699828286620026a4565b979650505050505050565b60608315620026b5575081620015ed565b825115620026c65782518084602001fd5b8160405162461bcd60e51b81526004016200086a919062002f01565b61117d80620032d983390190565b8280548282559060005260206000209081019282156200272e579160200282015b828111156200272e57825182559160200191906001019062002711565b506200273c92915062002798565b5090565b8280548282559060005260206000209081019282156200272e579160200282015b828111156200272e57825182546001600160a01b0319166001600160a01b0390911617825560209092019160019091019062002761565b5b808211156200273c576000815560010162002799565b80356001600160a01b03811681146200117557600080fd5b600082601f830112620027d8578081fd5b81356020620027f1620027eb83620030c9565b62003095565b80838252828201915082860187848660051b890101111562002811578586fd5b855b858110156200283a576200282782620027af565b8452928401929084019060010162002813565b5090979650505050505050565b600082601f83011262002858578081fd5b813560206200286b620027eb83620030c9565b80838252828201915082860187848660051b89010111156200288b578586fd5b855b858110156200283a578135845292840192908401906001016200288d565b600082601f830112620028bc578081fd5b81356020620028cf620027eb83620030c9565b80838252828201915082860187848660051b8901011115620028ef578586fd5b855b858110156200283a5762002905826200298a565b84529284019290840190600101620028f1565b600082601f83011262002929578081fd5b813567ffffffffffffffff811115620029465762002946620032b3565b6200295b601f8201601f191660200162003095565b81815284602083860101111562002970578283fd5b816020850160208301379081016020019190915292915050565b803560ff811681146200117557600080fd5b600060208284031215620029ae578081fd5b620009d982620027af565b600080600080600080600080610100898b031215620029d6578384fd5b883567ffffffffffffffff80821115620029ee578586fd5b620029fc8c838d01620027c7565b995060208b013591508082111562002a12578586fd5b62002a208c838d01620028ab565b985060408b013591508082111562002a36578586fd5b62002a448c838d0162002918565b975060608b013591508082111562002a5a578586fd5b5062002a698b828c0162002918565b9550506080890135935060a0890135925060c0890135915062002a8f60e08a01620027af565b90509295985092959890939650565b60008060006040848603121562002ab3578283fd5b833567ffffffffffffffff8082111562002acb578485fd5b818601915086601f83011262002adf578485fd5b81358181111562002aee578586fd5b8760208260051b850101111562002b03578586fd5b6020928301955093505084013562002b1b81620032c9565b809150509250925092565b6000602080838503121562002b39578182fd5b825167ffffffffffffffff81111562002b50578283fd5b8301601f8101851362002b61578283fd5b805162002b72620027eb82620030c9565b80828252848201915084840188868560051b870101111562002b92578687fd5b8694505b8385101562002bb657805183526001949094019391850191850162002b96565b50979650505050505050565b60008060006060848603121562002bd7578283fd5b833567ffffffffffffffff81111562002bee578384fd5b62002bfc8682870162002847565b9660208601359650604090950135949350505050565b60006020828403121562002c24578081fd5b8151620015ed81620032c9565b60006020828403121562002c43578081fd5b5035919050565b60006020828403121562002c5c578081fd5b5051919050565b60008060006060848603121562002c78578081fd5b83359250602084013567ffffffffffffffff81111562002c96578182fd5b62002ca48682870162002847565b925050604084013590509250925092565b6000806040838503121562002cc8578182fd5b50508035926020909101359150565b6000806040838503121562002cea578182fd5b8235915062002cfc602084016200298a565b90509250929050565b6000806000806080858703121562002d1b578182fd5b8435935062002d2d602086016200298a565b93969395505050506040820135916060013590565b60006020828403121562002d54578081fd5b620009d9826200298a565b60008060006060848603121562002d74578081fd5b62002d7f846200298a565b925062002d8f602085016200298a565b9150604084013590509250925092565b600080600080600060a0868803121562002db7578283fd5b62002dc2866200298a565b945062002dd2602087016200298a565b94979496505050506040830135926060810135926080909101359150565b6000815180845260208085019450808401835b8381101562002e215781518752958201959082019060010162002e03565b509495945050505050565b6000815180845262002e468160208601602086016200324c565b601f01601f19169290920160200192915050565b6000825162002e6e8184602087016200324c565b9190910192915050565b6020808252825182820181905260009190848201906040850190845b8181101562002ebb5783516001600160a01b03168352928401929184019160010162002e94565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b8181101562002ebb5783518352928401929184019160010162002ee3565b602081526000620009d9602083018462002e2c565b60408152600062002f2b604083018562002e2c565b828103602084015262002f3f818562002e2c565b95945050505050565b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252600790820152661d1a5b595bdd5d60ca1b604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b848152606060208201819052810183905260006001600160fb1b0384111562003026578081fd5b8360051b8086608085013782016080019081529115156040909101529392505050565b83815260606020820152600062003064606083018562002df0565b9050826040830152949350505050565b83815282602082015260606040820152600062002f3f606083018462002df0565b604051601f8201601f1916810167ffffffffffffffff81118282101715620030c157620030c1620032b3565b604052919050565b600067ffffffffffffffff821115620030e657620030e6620032b3565b5060051b60200190565b600082198211156200310657620031066200329d565b500190565b600181815b808511156200314c5781600019048211156200313057620031306200329d565b808516156200313e57918102915b93841c939080029062003110565b509250929050565b6000620009d983836000826200316d57506001620009dc565b816200317c57506000620009dc565b8160018114620031955760028114620031a057620031c0565b6001915050620009dc565b60ff841115620031b457620031b46200329d565b50506001821b620009dc565b5060208310610133831016604e8410600b8410161715620031e5575081810a620009dc565b620031f183836200310b565b80600019048211156200320857620032086200329d565b029392505050565b60008160001904831182151516156200322d576200322d6200329d565b500290565b6000828210156200324757620032476200329d565b500390565b60005b83811015620032695781810151838201526020016200324f565b8381111562003279576000848401525b50505050565b60006000198214156200329657620032966200329d565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b80151581146200214e57600080fdfe60806040523480156200001157600080fd5b506040516200117d3803806200117d833981016040819052620000349162000233565b818162000041336200008a565b815162000056906004906020850190620000da565b5080516200006c906005906020840190620000da565b5050600680546001600160a01b0319163317905550620002ed915050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b828054620000e8906200029a565b90600052602060002090601f0160209004810192826200010c576000855562000157565b82601f106200012757805160ff191683800117855562000157565b8280016001018555821562000157579182015b82811115620001575782518255916020019190600101906200013a565b506200016592915062000169565b5090565b5b808211156200016557600081556001016200016a565b600082601f83011262000191578081fd5b81516001600160401b0380821115620001ae57620001ae620002d7565b604051601f8301601f19908116603f01168101908282118183101715620001d957620001d9620002d7565b81604052838152602092508683858801011115620001f5578485fd5b8491505b83821015620002185785820183015181830184015290820190620001f9565b838211156200022957848385830101525b9695505050505050565b6000806040838503121562000246578182fd5b82516001600160401b03808211156200025d578384fd5b6200026b8683870162000180565b9350602085015191508082111562000281578283fd5b50620002908582860162000180565b9150509250929050565b600181811c90821680620002af57607f821691505b60208210811415620002d157634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052604160045260246000fd5b610e8080620002fd6000396000f3fe608060405234801561001057600080fd5b50600436106101165760003560e01c8063715018a6116100a257806395d89b411161007157806395d89b411461024b578063a457c2d714610253578063a9059cbb14610266578063dd62ed3e14610279578063f2fde38b146102b257600080fd5b8063715018a6146101f457806379cc6790146101fc5780638119c0651461020f5780638da5cb5b1461023a57600080fd5b8063313ce567116100e9578063313ce56714610181578063395093511461019057806340c10f19146101a357806342966c68146101b857806370a08231146101cb57600080fd5b806306fdde031461011b578063095ea7b31461013957806318160ddd1461015c57806323b872dd1461016e575b600080fd5b6101236102c5565b6040516101309190610d42565b60405180910390f35b61014c610147366004610d01565b610357565b6040519015158152602001610130565b6003545b604051908152602001610130565b61014c61017c366004610cc6565b61036d565b60405160128152602001610130565b61014c61019e366004610d01565b61041c565b6101b66101b1366004610d01565b610458565b005b6101b66101c6366004610d2a565b6104d1565b6101606101d9366004610c73565b6001600160a01b031660009081526001602052604090205490565b6101b66104de565b6101b661020a366004610d01565b610514565b600654610222906001600160a01b031681565b6040516001600160a01b039091168152602001610130565b6000546001600160a01b0316610222565b61012361059a565b61014c610261366004610d01565b6105a9565b61014c610274366004610d01565b610642565b610160610287366004610c94565b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205490565b6101b66102c0366004610c73565b61064f565b6060600480546102d490610df9565b80601f016020809104026020016040519081016040528092919081815260200182805461030090610df9565b801561034d5780601f106103225761010080835404028352916020019161034d565b820191906000526020600020905b81548152906001019060200180831161033057829003601f168201915b5050505050905090565b60006103643384846106e7565b50600192915050565b600061037a84848461080b565b6001600160a01b0384166000908152600260209081526040808320338452909152902054828110156104045760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084015b60405180910390fd5b61041185338584036106e7565b506001949350505050565b3360008181526002602090815260408083206001600160a01b03871684529091528120549091610364918590610453908690610dca565b6106e7565b6000546001600160a01b031633146104825760405162461bcd60e51b81526004016103fb90610d95565b600081116104c35760405162461bcd60e51b815260206004820152600e60248201526d1e995c9bd35a5b9d105b5bdd5b9d60921b60448201526064016103fb565b6104cd82826109da565b5050565b6104db3382610ab9565b50565b6000546001600160a01b031633146105085760405162461bcd60e51b81526004016103fb90610d95565b6105126000610c07565b565b60006105208333610287565b90508181101561057e5760405162461bcd60e51b8152602060048201526024808201527f45524332303a206275726e20616d6f756e74206578636565647320616c6c6f77604482015263616e636560e01b60648201526084016103fb565b61058b83338484036106e7565b6105958383610ab9565b505050565b6060600580546102d490610df9565b3360009081526002602090815260408083206001600160a01b03861684529091528120548281101561062b5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016103fb565b61063833858584036106e7565b5060019392505050565b600061036433848461080b565b6000546001600160a01b031633146106795760405162461bcd60e51b81526004016103fb90610d95565b6001600160a01b0381166106de5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016103fb565b6104db81610c07565b6001600160a01b0383166107495760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016103fb565b6001600160a01b0382166107aa5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016103fb565b6001600160a01b0383811660008181526002602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6001600160a01b03831661086f5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016103fb565b6001600160a01b0382166108d15760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016103fb565b6001600160a01b038316600090815260016020526040902054818110156109495760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016103fb565b6001600160a01b03808516600090815260016020526040808220858503905591851681529081208054849290610980908490610dca565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516109cc91815260200190565b60405180910390a350505050565b6001600160a01b038216610a305760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016103fb565b8060036000828254610a429190610dca565b90915550506001600160a01b03821660009081526001602052604081208054839290610a6f908490610dca565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b6001600160a01b038216610b195760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b60648201526084016103fb565b6001600160a01b03821660009081526001602052604090205481811015610b8d5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b60648201526084016103fb565b6001600160a01b0383166000908152600160205260408120838303905560038054849290610bbc908490610de2565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80356001600160a01b0381168114610c6e57600080fd5b919050565b600060208284031215610c84578081fd5b610c8d82610c57565b9392505050565b60008060408385031215610ca6578081fd5b610caf83610c57565b9150610cbd60208401610c57565b90509250929050565b600080600060608486031215610cda578081fd5b610ce384610c57565b9250610cf160208501610c57565b9150604084013590509250925092565b60008060408385031215610d13578182fd5b610d1c83610c57565b946020939093013593505050565b600060208284031215610d3b578081fd5b5035919050565b6000602080835283518082850152825b81811015610d6e57858101830151858201604001528201610d52565b81811115610d7f5783604083870101525b50601f01601f1916929092016040019392505050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60008219821115610ddd57610ddd610e34565b500190565b600082821015610df457610df4610e34565b500390565b600181811c90821680610e0d57607f821691505b60208210811415610e2e57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fdfea264697066735822122063d75c3ed6a55c09689ca75618623a1e87a6a2bc2069f1ad46d34952462455e264736f6c63430008040033a2646970667358221220e460b3ed68f54608f54796ec65456c2be94e9005459f6c2290495bbc86b571c164736f6c63430008040033
External libraries
StableSwapStorage : 0x3bccb81261aBa479C1ecaB28Aa92A248AE2e130E