Contract
0xcF70E2F3567ba396F3dd04822F78Ecd70Ba46894
2
Contract Overview
Balance:
0 ETH
ETH Value:
$0.00
My Name Tag:
Not Available
Txn Hash | Method |
Block
|
From
|
To
|
Value | [Txn Fee] | |||
---|---|---|---|---|---|---|---|---|---|
0x1582ebf7810889636e145a05f0618f57fe5488f5d37a5c160c81f7be56bdf02d | 0x6140b561 | 3073791 | 193 days 18 hrs ago | 0x5bdb37d0ddea3a90f233c7b7f6b9394b6b2eef34 | IN | Create: SwapUtils | 0 ETH | 0.176250948497 ETH |
[ Download CSV Export ]
Latest 25 internal transaction
[ Download CSV Export ]
Contract Name:
SwapUtils
Compiler Version
v0.6.12+commit.27d51765
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "./SwapUtils.sol"; /** * @title AmplificationUtils library * @notice A library to calculate and ramp the A parameter of a given `SwapUtils.Swap` struct. * This library assumes the struct is fully validated. */ library AmplificationUtils { using SafeMath for uint256; event RampA( uint256 oldA, uint256 newA, uint256 initialTime, uint256 futureTime ); event StopRampA(uint256 currentA, uint256 time); // Constant values used in ramping A calculations uint256 public constant A_PRECISION = 100; uint256 public constant MAX_A = 10**6; uint256 private constant MAX_A_CHANGE = 2; uint256 private constant MIN_RAMP_TIME = 14 days; /** * @notice Return A, the amplification coefficient * n * (n - 1) * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter */ function getA(SwapUtils.Swap storage self) external view returns (uint256) { return _getAPrecise(self).div(A_PRECISION); } /** * @notice Return A in its raw precision * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter in its raw precision form */ function getAPrecise(SwapUtils.Swap storage self) external view returns (uint256) { return _getAPrecise(self); } /** * @notice Return A in its raw precision * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter in its raw precision form */ function _getAPrecise(SwapUtils.Swap storage self) internal view returns (uint256) { uint256 t1 = self.futureATime; // time when ramp is finished uint256 a1 = self.futureA; // final A value when ramp is finished if (block.timestamp < t1) { uint256 t0 = self.initialATime; // time when ramp is started uint256 a0 = self.initialA; // initial A value when ramp is started if (a1 > a0) { // a0 + (a1 - a0) * (block.timestamp - t0) / (t1 - t0) return a0.add( a1.sub(a0).mul(block.timestamp.sub(t0)).div(t1.sub(t0)) ); } else { // a0 - (a0 - a1) * (block.timestamp - t0) / (t1 - t0) return a0.sub( a0.sub(a1).mul(block.timestamp.sub(t0)).div(t1.sub(t0)) ); } } else { return a1; } } /** * @notice Start ramping up or down A parameter towards given futureA_ and futureTime_ * Checks if the change is too rapid, and commits the new A value only when it falls under * the limit range. * @param self Swap struct to update * @param futureA_ the new A to ramp towards * @param futureTime_ timestamp when the new A should be reached */ function rampA( SwapUtils.Swap storage self, uint256 futureA_, uint256 futureTime_ ) external { require( block.timestamp >= self.initialATime.add(1 days), "Wait 1 day before starting ramp" ); require( futureTime_ >= block.timestamp.add(MIN_RAMP_TIME), "Insufficient ramp time" ); require( futureA_ > 0 && futureA_ < MAX_A, "futureA_ must be > 0 and < MAX_A" ); uint256 initialAPrecise = _getAPrecise(self); uint256 futureAPrecise = futureA_.mul(A_PRECISION); if (futureAPrecise < initialAPrecise) { require( futureAPrecise.mul(MAX_A_CHANGE) >= initialAPrecise, "futureA_ is too small" ); } else { require( futureAPrecise <= initialAPrecise.mul(MAX_A_CHANGE), "futureA_ is too large" ); } self.initialA = initialAPrecise; self.futureA = futureAPrecise; self.initialATime = block.timestamp; self.futureATime = futureTime_; emit RampA( initialAPrecise, futureAPrecise, block.timestamp, futureTime_ ); } /** * @notice Stops ramping A immediately. Once this function is called, rampA() * cannot be called for another 24 hours * @param self Swap struct to update */ function stopRampA(SwapUtils.Swap storage self) external { require(self.futureATime > block.timestamp, "Ramp is already stopped"); uint256 currentA = _getAPrecise(self); self.initialA = currentA; self.futureA = currentA; self.initialATime = block.timestamp; self.futureATime = block.timestamp; emit StopRampA(currentA, block.timestamp); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "./IERC20.sol"; import "../../math/SafeMath.sol"; import "../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "./AmplificationUtils.sol"; import "./LPToken.sol"; import "./MathUtils.sol"; /** * @title SwapUtils library * @notice A library to be used within Swap.sol. Contains functions responsible for custody and AMM functionalities. * @dev Contracts relying on this library must initialize SwapUtils.Swap struct then use this library * for SwapUtils.Swap struct. Note that this library contains both functions called by users and admins. * Admin functions should be protected within contracts using this library. */ library SwapUtils { using SafeERC20 for IERC20; using SafeMath for uint256; using MathUtils for uint256; /*** EVENTS ***/ event TokenSwap( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); event AddLiquidity( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event RemoveLiquidity( address indexed provider, uint256[] tokenAmounts, uint256 lpTokenSupply ); event RemoveLiquidityOne( address indexed provider, uint256 lpTokenAmount, uint256 lpTokenSupply, uint256 boughtId, uint256 tokensBought ); event RemoveLiquidityImbalance( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event NewAdminFee(uint256 newAdminFee); event NewSwapFee(uint256 newSwapFee); struct Swap { // variables around the ramp management of A, // the amplification coefficient * n * (n - 1) // see https://www.curve.fi/stableswap-paper.pdf for details uint256 initialA; uint256 futureA; uint256 initialATime; uint256 futureATime; // fee calculation uint256 swapFee; uint256 adminFee; LPToken lpToken; // contract references for all tokens being pooled IERC20[] pooledTokens; // multipliers for each pooled token's precision to get to POOL_PRECISION_DECIMALS // for example, TBTC has 18 decimals, so the multiplier should be 1. WBTC // has 8, so the multiplier should be 10 ** 18 / 10 ** 8 => 10 ** 10 uint256[] tokenPrecisionMultipliers; // the pool balance of each token, in the token's precision // the contract's actual token balance might differ uint256[] balances; } // Struct storing variables used in calculations in the // calculateWithdrawOneTokenDY function to avoid stack too deep errors struct CalculateWithdrawOneTokenDYInfo { uint256 d0; uint256 d1; uint256 newY; uint256 feePerToken; uint256 preciseA; } // Struct storing variables used in calculations in the // {add,remove}Liquidity functions to avoid stack too deep errors struct ManageLiquidityInfo { uint256 d0; uint256 d1; uint256 d2; uint256 preciseA; LPToken lpToken; uint256 totalSupply; uint256[] balances; uint256[] multipliers; } // the precision all pools tokens will be converted to uint8 public constant POOL_PRECISION_DECIMALS = 18; // the denominator used to calculate admin and LP fees. For example, an // LP fee might be something like tradeAmount.mul(fee).div(FEE_DENOMINATOR) uint256 private constant FEE_DENOMINATOR = 10**10; // Max swap fee is 1% or 100bps of each swap uint256 public constant MAX_SWAP_FEE = 10**8; // Max adminFee is 100% of the swapFee // adminFee does not add additional fee on top of swapFee // Instead it takes a certain % of the swapFee. Therefore it has no impact on the // users but only on the earnings of LPs uint256 public constant MAX_ADMIN_FEE = 10**10; // Constant value used as max loop limit uint256 private constant MAX_LOOP_LIMIT = 256; /*** VIEW & PURE FUNCTIONS ***/ function _getAPrecise(Swap storage self) internal view returns (uint256) { return AmplificationUtils._getAPrecise(self); } /** * @notice Calculate the dy, the amount of selected token that user receives and * the fee of withdrawing in one token * @param tokenAmount the amount to withdraw in the pool's precision * @param tokenIndex which token will be withdrawn * @param self Swap struct to read from * @return the amount of token user will receive */ function calculateWithdrawOneToken( Swap storage self, uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256) { (uint256 availableTokenAmount, ) = _calculateWithdrawOneToken( self, tokenAmount, tokenIndex, self.lpToken.totalSupply() ); return availableTokenAmount; } function _calculateWithdrawOneToken( Swap storage self, uint256 tokenAmount, uint8 tokenIndex, uint256 totalSupply ) internal view returns (uint256, uint256) { uint256 dy; uint256 newY; uint256 currentY; (dy, newY, currentY) = calculateWithdrawOneTokenDY( self, tokenIndex, tokenAmount, totalSupply ); // dy_0 (without fees) // dy, dy_0 - dy uint256 dySwapFee = currentY .sub(newY) .div(self.tokenPrecisionMultipliers[tokenIndex]) .sub(dy); return (dy, dySwapFee); } /** * @notice Calculate the dy of withdrawing in one token * @param self Swap struct to read from * @param tokenIndex which token will be withdrawn * @param tokenAmount the amount to withdraw in the pools precision * @return the d and the new y after withdrawing one token */ function calculateWithdrawOneTokenDY( Swap storage self, uint8 tokenIndex, uint256 tokenAmount, uint256 totalSupply ) internal view returns ( uint256, uint256, uint256 ) { // Get the current D, then solve the stableswap invariant // y_i for D - tokenAmount uint256[] memory xp = _xp(self); require(tokenIndex < xp.length, "Token index out of range"); CalculateWithdrawOneTokenDYInfo memory v = CalculateWithdrawOneTokenDYInfo(0, 0, 0, 0, 0); v.preciseA = _getAPrecise(self); v.d0 = getD(xp, v.preciseA); v.d1 = v.d0.sub(tokenAmount.mul(v.d0).div(totalSupply)); require(tokenAmount <= xp[tokenIndex], "Withdraw exceeds available"); v.newY = getYD(v.preciseA, tokenIndex, xp, v.d1); uint256[] memory xpReduced = new uint256[](xp.length); v.feePerToken = _feePerToken(self.swapFee, xp.length); for (uint256 i = 0; i < xp.length; i++) { uint256 xpi = xp[i]; // if i == tokenIndex, dxExpected = xp[i] * d1 / d0 - newY // else dxExpected = xp[i] - (xp[i] * d1 / d0) // xpReduced[i] -= dxExpected * fee / FEE_DENOMINATOR xpReduced[i] = xpi.sub( ( (i == tokenIndex) ? xpi.mul(v.d1).div(v.d0).sub(v.newY) : xpi.sub(xpi.mul(v.d1).div(v.d0)) ).mul(v.feePerToken).div(FEE_DENOMINATOR) ); } uint256 dy = xpReduced[tokenIndex].sub( getYD(v.preciseA, tokenIndex, xpReduced, v.d1) ); dy = dy.sub(1).div(self.tokenPrecisionMultipliers[tokenIndex]); return (dy, v.newY, xp[tokenIndex]); } /** * @notice Calculate the price of a token in the pool with given * precision-adjusted balances and a particular D. * * @dev This is accomplished via solving the invariant iteratively. * See the StableSwap paper and Curve.fi implementation for further details. * * x_1**2 + x1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A) * x_1**2 + b*x_1 = c * x_1 = (x_1**2 + c) / (2*x_1 + b) * * @param a the amplification coefficient * n * (n - 1). See the StableSwap paper for details. * @param tokenIndex Index of token we are calculating for. * @param xp a precision-adjusted set of pool balances. Array should be * the same cardinality as the pool. * @param d the stableswap invariant * @return the price of the token, in the same precision as in xp */ function getYD( uint256 a, uint8 tokenIndex, uint256[] memory xp, uint256 d ) internal pure returns (uint256) { uint256 numTokens = xp.length; require(tokenIndex < numTokens, "Token not found"); uint256 c = d; uint256 s; uint256 nA = a.mul(numTokens); for (uint256 i = 0; i < numTokens; i++) { if (i != tokenIndex) { s = s.add(xp[i]); c = c.mul(d).div(xp[i].mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // c = c * D * D * D * ... overflow! } } c = c.mul(d).mul(AmplificationUtils.A_PRECISION).div(nA.mul(numTokens)); uint256 b = s.add(d.mul(AmplificationUtils.A_PRECISION).div(nA)); uint256 yPrev; uint256 y = d; for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { yPrev = y; y = y.mul(y).add(c).div(y.mul(2).add(b).sub(d)); if (y.within1(yPrev)) { return y; } } revert("Approximation did not converge"); } /** * @notice Get D, the StableSwap invariant, based on a set of balances and a particular A. * @param xp a precision-adjusted set of pool balances. Array should be the same cardinality * as the pool. * @param a the amplification coefficient * n * (n - 1) in A_PRECISION. * See the StableSwap paper for details * @return the invariant, at the precision of the pool */ function getD(uint256[] memory xp, uint256 a) internal pure returns (uint256) { uint256 numTokens = xp.length; uint256 s; for (uint256 i = 0; i < numTokens; i++) { s = s.add(xp[i]); } if (s == 0) { return 0; } uint256 prevD; uint256 d = s; uint256 nA = a.mul(numTokens); for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { uint256 dP = d; for (uint256 j = 0; j < numTokens; j++) { dP = dP.mul(d).div(xp[j].mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // dP = dP * D * D * D * ... overflow! } prevD = d; d = nA .mul(s) .div(AmplificationUtils.A_PRECISION) .add(dP.mul(numTokens)) .mul(d) .div( nA .sub(AmplificationUtils.A_PRECISION) .mul(d) .div(AmplificationUtils.A_PRECISION) .add(numTokens.add(1).mul(dP)) ); if (d.within1(prevD)) { return d; } } // Convergence should occur in 4 loops or less. If this is reached, there may be something wrong // with the pool. If this were to occur repeatedly, LPs should withdraw via `removeLiquidity()` // function which does not rely on D. revert("D does not converge"); } /** * @notice Given a set of balances and precision multipliers, return the * precision-adjusted balances. * * @param balances an array of token balances, in their native precisions. * These should generally correspond with pooled tokens. * * @param precisionMultipliers an array of multipliers, corresponding to * the amounts in the balances array. When multiplied together they * should yield amounts at the pool's precision. * * @return an array of amounts "scaled" to the pool's precision */ function _xp( uint256[] memory balances, uint256[] memory precisionMultipliers ) internal pure returns (uint256[] memory) { uint256 numTokens = balances.length; require( numTokens == precisionMultipliers.length, "Balances must match multipliers" ); uint256[] memory xp = new uint256[](numTokens); for (uint256 i = 0; i < numTokens; i++) { xp[i] = balances[i].mul(precisionMultipliers[i]); } return xp; } /** * @notice Return the precision-adjusted balances of all tokens in the pool * @param self Swap struct to read from * @return the pool balances "scaled" to the pool's precision, allowing * them to be more easily compared. */ function _xp(Swap storage self) internal view returns (uint256[] memory) { return _xp(self.balances, self.tokenPrecisionMultipliers); } /** * @notice Get the virtual price, to help calculate profit * @param self Swap struct to read from * @return the virtual price, scaled to precision of POOL_PRECISION_DECIMALS */ function getVirtualPrice(Swap storage self) external view returns (uint256) { uint256 d = getD(_xp(self), _getAPrecise(self)); LPToken lpToken = self.lpToken; uint256 supply = lpToken.totalSupply(); if (supply > 0) { return d.mul(10**uint256(POOL_PRECISION_DECIMALS)).div(supply); } return 0; } /** * @notice Calculate the new balances of the tokens given the indexes of the token * that is swapped from (FROM) and the token that is swapped to (TO). * This function is used as a helper function to calculate how much TO token * the user should receive on swap. * * @param preciseA precise form of amplification coefficient * @param tokenIndexFrom index of FROM token * @param tokenIndexTo index of TO token * @param x the new total amount of FROM token * @param xp balances of the tokens in the pool * @return the amount of TO token that should remain in the pool */ function getY( uint256 preciseA, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 x, uint256[] memory xp ) internal pure returns (uint256) { uint256 numTokens = xp.length; require( tokenIndexFrom != tokenIndexTo, "Can't compare token to itself" ); require( tokenIndexFrom < numTokens && tokenIndexTo < numTokens, "Tokens must be in pool" ); uint256 d = getD(xp, preciseA); uint256 c = d; uint256 s; uint256 nA = numTokens.mul(preciseA); uint256 _x; for (uint256 i = 0; i < numTokens; i++) { if (i == tokenIndexFrom) { _x = x; } else if (i != tokenIndexTo) { _x = xp[i]; } else { continue; } s = s.add(_x); c = c.mul(d).div(_x.mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // c = c * D * D * D * ... overflow! } c = c.mul(d).mul(AmplificationUtils.A_PRECISION).div(nA.mul(numTokens)); uint256 b = s.add(d.mul(AmplificationUtils.A_PRECISION).div(nA)); uint256 yPrev; uint256 y = d; // iterative approximation for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { yPrev = y; y = y.mul(y).add(c).div(y.mul(2).add(b).sub(d)); if (y.within1(yPrev)) { return y; } } revert("Approximation did not converge"); } /** * @notice Externally calculates a swap between two tokens. * @param self Swap struct to read from * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @return dy the number of tokens the user will get */ function calculateSwap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256 dy) { (dy, ) = _calculateSwap( self, tokenIndexFrom, tokenIndexTo, dx, self.balances ); } /** * @notice Internally calculates a swap between two tokens. * * @dev The caller is expected to transfer the actual amounts (dx and dy) * using the token contracts. * * @param self Swap struct to read from * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @return dy the number of tokens the user will get * @return dyFee the associated fee */ function _calculateSwap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256[] memory balances ) internal view returns (uint256 dy, uint256 dyFee) { uint256[] memory multipliers = self.tokenPrecisionMultipliers; uint256[] memory xp = _xp(balances, multipliers); require( tokenIndexFrom < xp.length && tokenIndexTo < xp.length, "Token index out of range" ); uint256 x = dx.mul(multipliers[tokenIndexFrom]).add(xp[tokenIndexFrom]); uint256 y = getY( _getAPrecise(self), tokenIndexFrom, tokenIndexTo, x, xp ); dy = xp[tokenIndexTo].sub(y).sub(1); dyFee = dy.mul(self.swapFee).div(FEE_DENOMINATOR); dy = dy.sub(dyFee).div(multipliers[tokenIndexTo]); } /** * @notice A simple method to calculate amount of each underlying * tokens that is returned upon burning given amount of * LP tokens * * @param amount the amount of LP tokens that would to be burned on * withdrawal * @return array of amounts of tokens user will receive */ function calculateRemoveLiquidity(Swap storage self, uint256 amount) external view returns (uint256[] memory) { return _calculateRemoveLiquidity( self.balances, amount, self.lpToken.totalSupply() ); } function _calculateRemoveLiquidity( uint256[] memory balances, uint256 amount, uint256 totalSupply ) internal pure returns (uint256[] memory) { require(amount <= totalSupply, "Cannot exceed total supply"); uint256[] memory amounts = new uint256[](balances.length); for (uint256 i = 0; i < balances.length; i++) { amounts[i] = balances[i].mul(amount).div(totalSupply); } return amounts; } /** * @notice A simple method to calculate prices from deposits or * withdrawals, excluding fees but including slippage. This is * helpful as an input into the various "min" parameters on calls * to fight front-running * * @dev This shouldn't be used outside frontends for user estimates. * * @param self Swap struct to read from * @param amounts an array of token amounts to deposit or withdrawal, * corresponding to pooledTokens. The amount should be in each * pooled token's native precision. If a token charges a fee on transfers, * use the amount that gets transferred after the fee. * @param deposit whether this is a deposit or a withdrawal * @return if deposit was true, total amount of lp token that will be minted and if * deposit was false, total amount of lp token that will be burned */ function calculateTokenAmount( Swap storage self, uint256[] calldata amounts, bool deposit ) external view returns (uint256) { uint256 a = _getAPrecise(self); uint256[] memory balances = self.balances; uint256[] memory multipliers = self.tokenPrecisionMultipliers; uint256 d0 = getD(_xp(balances, multipliers), a); for (uint256 i = 0; i < balances.length; i++) { if (deposit) { balances[i] = balances[i].add(amounts[i]); } else { balances[i] = balances[i].sub( amounts[i], "Cannot withdraw more than available" ); } } uint256 d1 = getD(_xp(balances, multipliers), a); uint256 totalSupply = self.lpToken.totalSupply(); if (deposit) { return d1.sub(d0).mul(totalSupply).div(d0); } else { return d0.sub(d1).mul(totalSupply).div(d0); } } /** * @notice return accumulated amount of admin fees of the token with given index * @param self Swap struct to read from * @param index Index of the pooled token * @return admin balance in the token's precision */ function getAdminBalance(Swap storage self, uint256 index) external view returns (uint256) { require(index < self.pooledTokens.length, "Token index out of range"); return self.pooledTokens[index].balanceOf(address(this)).sub( self.balances[index] ); } /** * @notice internal helper function to calculate fee per token multiplier used in * swap fee calculations * @param swapFee swap fee for the tokens * @param numTokens number of tokens pooled */ function _feePerToken(uint256 swapFee, uint256 numTokens) internal pure returns (uint256) { return swapFee.mul(numTokens).div(numTokens.sub(1).mul(4)); } /*** STATE MODIFYING FUNCTIONS ***/ /** * @notice swap two tokens in the pool * @param self Swap struct to read from and write to * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell * @param minDy the min amount the user would like to receive, or revert. * @return amount of token user received on swap */ function swap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy ) external returns (uint256) { { IERC20 tokenFrom = self.pooledTokens[tokenIndexFrom]; require( dx <= tokenFrom.balanceOf(msg.sender), "Cannot swap more than you own" ); // Transfer tokens first to see if a fee was charged on transfer uint256 beforeBalance = tokenFrom.balanceOf(address(this)); tokenFrom.safeTransferFrom(msg.sender, address(this), dx); // Use the actual transferred amount for AMM math dx = tokenFrom.balanceOf(address(this)).sub(beforeBalance); } uint256 dy; uint256 dyFee; uint256[] memory balances = self.balances; (dy, dyFee) = _calculateSwap( self, tokenIndexFrom, tokenIndexTo, dx, balances ); require(dy >= minDy, "Swap didn't result in min tokens"); uint256 dyAdminFee = dyFee.mul(self.adminFee).div(FEE_DENOMINATOR).div( self.tokenPrecisionMultipliers[tokenIndexTo] ); self.balances[tokenIndexFrom] = balances[tokenIndexFrom].add(dx); self.balances[tokenIndexTo] = balances[tokenIndexTo].sub(dy).sub( dyAdminFee ); self.pooledTokens[tokenIndexTo].safeTransfer(msg.sender, dy); emit TokenSwap(msg.sender, dx, dy, tokenIndexFrom, tokenIndexTo); return dy; } /** * @notice Add liquidity to the pool * @param self Swap struct to read from and write to * @param amounts the amounts of each token to add, in their native precision * @param minToMint the minimum LP tokens adding this amount of liquidity * should mint, otherwise revert. Handy for front-running mitigation * allowed addresses. If the pool is not in the guarded launch phase, this parameter will be ignored. * @return amount of LP token user received */ function addLiquidity( Swap storage self, uint256[] memory amounts, uint256 minToMint ) external returns (uint256) { IERC20[] memory pooledTokens = self.pooledTokens; require( amounts.length == pooledTokens.length, "Amounts must match pooled tokens" ); // current state ManageLiquidityInfo memory v = ManageLiquidityInfo( 0, 0, 0, _getAPrecise(self), self.lpToken, 0, self.balances, self.tokenPrecisionMultipliers ); v.totalSupply = v.lpToken.totalSupply(); if (v.totalSupply != 0) { v.d0 = getD(_xp(v.balances, v.multipliers), v.preciseA); } uint256[] memory newBalances = new uint256[](pooledTokens.length); for (uint256 i = 0; i < pooledTokens.length; i++) { require( v.totalSupply != 0 || amounts[i] > 0, "Must supply all tokens in pool" ); // Transfer tokens first to see if a fee was charged on transfer if (amounts[i] != 0) { uint256 beforeBalance = pooledTokens[i].balanceOf( address(this) ); pooledTokens[i].safeTransferFrom( msg.sender, address(this), amounts[i] ); // Update the amounts[] with actual transfer amount amounts[i] = pooledTokens[i].balanceOf(address(this)).sub( beforeBalance ); } newBalances[i] = v.balances[i].add(amounts[i]); } // invariant after change v.d1 = getD(_xp(newBalances, v.multipliers), v.preciseA); require(v.d1 > v.d0, "D should increase"); // updated to reflect fees and calculate the user's LP tokens v.d2 = v.d1; uint256[] memory fees = new uint256[](pooledTokens.length); if (v.totalSupply != 0) { uint256 feePerToken = _feePerToken( self.swapFee, pooledTokens.length ); for (uint256 i = 0; i < pooledTokens.length; i++) { uint256 idealBalance = v.d1.mul(v.balances[i]).div(v.d0); fees[i] = feePerToken .mul(idealBalance.difference(newBalances[i])) .div(FEE_DENOMINATOR); self.balances[i] = newBalances[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); newBalances[i] = newBalances[i].sub(fees[i]); } v.d2 = getD(_xp(newBalances, v.multipliers), v.preciseA); } else { // the initial depositor doesn't pay fees self.balances = newBalances; } uint256 toMint; if (v.totalSupply == 0) { toMint = v.d1; } else { toMint = v.d2.sub(v.d0).mul(v.totalSupply).div(v.d0); } require(toMint >= minToMint, "Couldn't mint min requested"); // mint the user's LP tokens v.lpToken.mint(msg.sender, toMint); emit AddLiquidity( msg.sender, amounts, fees, v.d1, v.totalSupply.add(toMint) ); return toMint; } /** * @notice Burn LP tokens to remove liquidity from the pool. * @dev Liquidity can always be removed, even when the pool is paused. * @param self Swap struct to read from and write to * @param amount the amount of LP tokens to burn * @param minAmounts the minimum amounts of each token in the pool * acceptable for this burn. Useful as a front-running mitigation * @return amounts of tokens the user received */ function removeLiquidity( Swap storage self, uint256 amount, uint256[] calldata minAmounts ) external returns (uint256[] memory) { LPToken lpToken = self.lpToken; IERC20[] memory pooledTokens = self.pooledTokens; require(amount <= lpToken.balanceOf(msg.sender), ">LP.balanceOf"); require( minAmounts.length == pooledTokens.length, "minAmounts must match poolTokens" ); uint256[] memory balances = self.balances; uint256 totalSupply = lpToken.totalSupply(); uint256[] memory amounts = _calculateRemoveLiquidity( balances, amount, totalSupply ); for (uint256 i = 0; i < amounts.length; i++) { require(amounts[i] >= minAmounts[i], "amounts[i] < minAmounts[i]"); self.balances[i] = balances[i].sub(amounts[i]); pooledTokens[i].safeTransfer(msg.sender, amounts[i]); } lpToken.burnFrom(msg.sender, amount); emit RemoveLiquidity(msg.sender, amounts, totalSupply.sub(amount)); return amounts; } /** * @notice Remove liquidity from the pool all in one token. * @param self Swap struct to read from and write to * @param tokenAmount the amount of the lp tokens to burn * @param tokenIndex the index of the token you want to receive * @param minAmount the minimum amount to withdraw, otherwise revert * @return amount chosen token that user received */ function removeLiquidityOneToken( Swap storage self, uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount ) external returns (uint256) { LPToken lpToken = self.lpToken; IERC20[] memory pooledTokens = self.pooledTokens; require(tokenAmount <= lpToken.balanceOf(msg.sender), ">LP.balanceOf"); require(tokenIndex < pooledTokens.length, "Token not found"); uint256 totalSupply = lpToken.totalSupply(); (uint256 dy, uint256 dyFee) = _calculateWithdrawOneToken( self, tokenAmount, tokenIndex, totalSupply ); require(dy >= minAmount, "dy < minAmount"); self.balances[tokenIndex] = self.balances[tokenIndex].sub( dy.add(dyFee.mul(self.adminFee).div(FEE_DENOMINATOR)) ); lpToken.burnFrom(msg.sender, tokenAmount); pooledTokens[tokenIndex].safeTransfer(msg.sender, dy); emit RemoveLiquidityOne( msg.sender, tokenAmount, totalSupply, tokenIndex, dy ); return dy; } /** * @notice Remove liquidity from the pool, weighted differently than the * pool's current balances. * * @param self Swap struct to read from and write to * @param amounts how much of each token to withdraw * @param maxBurnAmount the max LP token provider is willing to pay to * remove liquidity. Useful as a front-running mitigation. * @return actual amount of LP tokens burned in the withdrawal */ function removeLiquidityImbalance( Swap storage self, uint256[] memory amounts, uint256 maxBurnAmount ) public returns (uint256) { ManageLiquidityInfo memory v = ManageLiquidityInfo( 0, 0, 0, _getAPrecise(self), self.lpToken, 0, self.balances, self.tokenPrecisionMultipliers ); v.totalSupply = v.lpToken.totalSupply(); IERC20[] memory pooledTokens = self.pooledTokens; require( amounts.length == pooledTokens.length, "Amounts should match pool tokens" ); require( maxBurnAmount <= v.lpToken.balanceOf(msg.sender) && maxBurnAmount != 0, ">LP.balanceOf" ); uint256 feePerToken = _feePerToken(self.swapFee, pooledTokens.length); uint256[] memory fees = new uint256[](pooledTokens.length); { uint256[] memory balances1 = new uint256[](pooledTokens.length); v.d0 = getD(_xp(v.balances, v.multipliers), v.preciseA); for (uint256 i = 0; i < pooledTokens.length; i++) { balances1[i] = v.balances[i].sub( amounts[i], "Cannot withdraw more than available" ); } v.d1 = getD(_xp(balances1, v.multipliers), v.preciseA); for (uint256 i = 0; i < pooledTokens.length; i++) { uint256 idealBalance = v.d1.mul(v.balances[i]).div(v.d0); uint256 difference = idealBalance.difference(balances1[i]); fees[i] = feePerToken.mul(difference).div(FEE_DENOMINATOR); self.balances[i] = balances1[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); balances1[i] = balances1[i].sub(fees[i]); } v.d2 = getD(_xp(balances1, v.multipliers), v.preciseA); } uint256 tokenAmount = v.d0.sub(v.d2).mul(v.totalSupply).div(v.d0); require(tokenAmount != 0, "Burnt amount cannot be zero"); tokenAmount = tokenAmount.add(1); require(tokenAmount <= maxBurnAmount, "tokenAmount > maxBurnAmount"); v.lpToken.burnFrom(msg.sender, tokenAmount); for (uint256 i = 0; i < pooledTokens.length; i++) { pooledTokens[i].safeTransfer(msg.sender, amounts[i]); } emit RemoveLiquidityImbalance( msg.sender, amounts, fees, v.d1, v.totalSupply.sub(tokenAmount) ); return tokenAmount; } /** * @notice withdraw all admin fees to a given address * @param self Swap struct to withdraw fees from * @param to Address to send the fees to */ function withdrawAdminFees(Swap storage self, address to) external { IERC20[] memory pooledTokens = self.pooledTokens; for (uint256 i = 0; i < pooledTokens.length; i++) { IERC20 token = pooledTokens[i]; uint256 balance = token.balanceOf(address(this)).sub( self.balances[i] ); if (balance != 0) { token.safeTransfer(to, balance); } } } /** * @notice Sets the admin fee * @dev adminFee cannot be higher than 100% of the swap fee * @param self Swap struct to update * @param newAdminFee new admin fee to be applied on future transactions */ function setAdminFee(Swap storage self, uint256 newAdminFee) external { require(newAdminFee <= MAX_ADMIN_FEE, "Fee is too high"); self.adminFee = newAdminFee; emit NewAdminFee(newAdminFee); } /** * @notice update the swap fee * @dev fee cannot be higher than 1% of each swap * @param self Swap struct to update * @param newSwapFee new swap fee to be applied on future transactions */ function setSwapFee(Swap storage self, uint256 newSwapFee) external { require(newSwapFee <= MAX_SWAP_FEE, "Fee is too high"); self.swapFee = newSwapFee; emit NewSwapFee(newSwapFee); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a % b; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20BurnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "./interfaces/ISwap.sol"; /** * @title Liquidity Provider Token * @notice This token is an ERC20 detailed token with added capability to be minted by the owner. * It is used to represent user's shares when providing liquidity to swap contracts. * @dev Only Swap contracts should initialize and own LPToken contracts. */ contract LPToken is ERC20BurnableUpgradeable, OwnableUpgradeable { using SafeMathUpgradeable for uint256; /** * @notice Initializes this LPToken contract with the given name and symbol * @dev The caller of this function will become the owner. A Swap contract should call this * in its initializer function. * @param name name of this token * @param symbol symbol of this token */ function initialize(string memory name, string memory symbol) external initializer returns (bool) { __Context_init_unchained(); __ERC20_init_unchained(name, symbol); __Ownable_init_unchained(); return true; } /** * @notice Mints the given amount of LPToken to the recipient. * @dev only owner can call this mint function * @param recipient address of account to receive the tokens * @param amount amount of tokens to mint */ function mint(address recipient, uint256 amount) external onlyOwner { require(amount != 0, "LPToken: cannot mint 0"); _mint(recipient, amount); } /** * @dev Overrides ERC20._beforeTokenTransfer() which get called on every transfers including * minting and burning. This ensures that Swap.updateUserWithdrawFees are called everytime. * This assumes the owner is set to a Swap contract's address. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual override(ERC20Upgradeable) { super._beforeTokenTransfer(from, to, amount); require(to != address(this), "LPToken: cannot send to itself"); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; /** * @title MathUtils library * @notice A library to be used in conjunction with SafeMath. Contains functions for calculating * differences between two uint256. */ library MathUtils { /** * @notice Compares a and b and returns true if the difference between a and b * is less than 1 or equal to each other. * @param a uint256 to compare with * @param b uint256 to compare with * @return True if the difference between a and b is less than 1 or equal, * otherwise return false */ function within1(uint256 a, uint256 b) internal pure returns (bool) { return (difference(a, b) <= 1); } /** * @notice Calculates absolute difference between a and b * @param a uint256 to compare with * @param b uint256 to compare with * @return Difference between a and b */ function difference(uint256 a, uint256 b) internal pure returns (uint256) { if (a > b) { return a - b; } return b - a; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../../utils/ContextUpgradeable.sol"; import "./ERC20Upgradeable.sol"; import "../../proxy/Initializable.sol"; /** * @dev Extension of {ERC20} that allows token holders to destroy both their own * tokens and those that they have an allowance for, in a way that can be * recognized off-chain (via event analysis). */ abstract contract ERC20BurnableUpgradeable is Initializable, ContextUpgradeable, ERC20Upgradeable { function __ERC20Burnable_init() internal initializer { __Context_init_unchained(); __ERC20Burnable_init_unchained(); } function __ERC20Burnable_init_unchained() internal initializer { } using SafeMathUpgradeable for uint256; /** * @dev Destroys `amount` tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 amount) public virtual { _burn(_msgSender(), amount); } /** * @dev Destroys `amount` tokens from `account`, deducting from the caller's * allowance. * * See {ERC20-_burn} and {ERC20-allowance}. * * Requirements: * * - the caller must have allowance for ``accounts``'s tokens of at least * `amount`. */ function burnFrom(address account, uint256 amount) public virtual { uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance"); _approve(account, _msgSender(), decreasedAllowance); _burn(account, amount); } uint256[50] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../utils/ContextUpgradeable.sol"; import "../proxy/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ function __Ownable_init() internal initializer { __Context_init_unchained(); __Ownable_init_unchained(); } function __Ownable_init_unchained() internal initializer { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } uint256[49] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "./IAllowlist.sol"; interface ISwap { // pool data view functions function getA() external view returns (uint256); function getAllowlist() external view returns (IAllowlist); function getToken(uint8 index) external view returns (IERC20); function getTokenIndex(address tokenAddress) external view returns (uint8); function getTokenBalance(uint8 index) external view returns (uint256); function getVirtualPrice() external view returns (uint256); function isGuarded() external view returns (bool); // min return calculation functions function calculateSwap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256); function calculateTokenAmount(uint256[] calldata amounts, bool deposit) external view returns (uint256); function calculateRemoveLiquidity(uint256 amount) external view returns (uint256[] memory); function calculateRemoveLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256 availableTokenAmount); // state modifying functions function initialize( IERC20[] memory pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 a, uint256 fee, uint256 adminFee, address lpTokenTargetAddress ) external; function swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external returns (uint256); function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline ) external returns (uint256); function removeLiquidity( uint256 amount, uint256[] calldata minAmounts, uint256 deadline ) external returns (uint256[] memory); function removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline ) external returns (uint256); function removeLiquidityImbalance( uint256[] calldata amounts, uint256 maxBurnAmount, uint256 deadline ) external returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../proxy/Initializable.sol"; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal initializer { __Context_init_unchained(); } function __Context_init_unchained() internal initializer { } function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } uint256[50] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../../utils/ContextUpgradeable.sol"; import "./IERC20Upgradeable.sol"; import "../../math/SafeMathUpgradeable.sol"; import "../../proxy/Initializable.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable { using SafeMathUpgradeable for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ function __ERC20_init(string memory name_, string memory symbol_) internal initializer { __Context_init_unchained(); __ERC20_init_unchained(name_, symbol_); } function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer { _name = name_; _symbol = symbol_; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() public view virtual returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal virtual { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } uint256[44] private __gap; }
// SPDX-License-Identifier: MIT // solhint-disable-next-line compiler-version pragma solidity >=0.4.24 <0.8.0; import "../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Modifier to protect an initializer function from being invoked twice. */ modifier initializer() { require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized"); bool isTopLevelCall = !_initializing; if (isTopLevelCall) { _initializing = true; _initialized = true; } _; if (isTopLevelCall) { _initializing = false; } } /// @dev Returns true if and only if the function is running in the constructor function _isConstructor() private view returns (bool) { return !AddressUpgradeable.isContract(address(this)); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20Upgradeable { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMathUpgradeable { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a % b; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../../utils/Context.sol"; import "./IERC20.sol"; import "../../math/SafeMath.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name_, string memory symbol_) public { _name = name_; _symbol = symbol_; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() public view virtual returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal virtual { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IAllowlist { function getPoolAccountLimit(address poolAddress) external view returns (uint256); function getPoolCap(address poolAddress) external view returns (uint256); function verifyAddress(address account, bytes32[] calldata merkleProof) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts/proxy/Clones.sol"; import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import "./OwnerPausableUpgradeable.sol"; import "./SwapUtils.sol"; import "./AmplificationUtils.sol"; /** * @title Swap - A StableSwap implementation in solidity. * @notice This contract is responsible for custody of closely pegged assets (eg. group of stablecoins) * and automatic market making system. Users become an LP (Liquidity Provider) by depositing their tokens * in desired ratios for an exchange of the pool token that represents their share of the pool. * Users can burn pool tokens and withdraw their share of token(s). * * Each time a swap between the pooled tokens happens, a set fee incurs which effectively gets * distributed to the LPs. * * In case of emergencies, admin can pause additional deposits, swaps, or single-asset withdraws - which * stops the ratio of the tokens in the pool from changing. * Users can always withdraw their tokens via multi-asset withdraws. * * @dev Most of the logic is stored as a library `SwapUtils` for the sake of reducing contract's * deployment size. */ contract Swap is OwnerPausableUpgradeable, ReentrancyGuardUpgradeable { using SafeERC20 for IERC20; using SafeMath for uint256; using SwapUtils for SwapUtils.Swap; using AmplificationUtils for SwapUtils.Swap; // Struct storing data responsible for automatic market maker functionalities. In order to // access this data, this contract uses SwapUtils library. For more details, see SwapUtils.sol SwapUtils.Swap public swapStorage; // Maps token address to an index in the pool. Used to prevent duplicate tokens in the pool. // getTokenIndex function also relies on this mapping to retrieve token index. mapping(address => uint8) private tokenIndexes; /*** EVENTS ***/ // events replicated from SwapUtils to make the ABI easier for dumb // clients event TokenSwap( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); event AddLiquidity( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event RemoveLiquidity( address indexed provider, uint256[] tokenAmounts, uint256 lpTokenSupply ); event RemoveLiquidityOne( address indexed provider, uint256 lpTokenAmount, uint256 lpTokenSupply, uint256 boughtId, uint256 tokensBought ); event RemoveLiquidityImbalance( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event NewAdminFee(uint256 newAdminFee); event NewSwapFee(uint256 newSwapFee); event NewWithdrawFee(uint256 newWithdrawFee); event RampA( uint256 oldA, uint256 newA, uint256 initialTime, uint256 futureTime ); event StopRampA(uint256 currentA, uint256 time); /** * @notice Initializes this Swap contract with the given parameters. * This will also clone a LPToken contract that represents users' * LP positions. The owner of LPToken will be this contract - which means * only this contract is allowed to mint/burn tokens. * * @param _pooledTokens an array of ERC20s this pool will accept * @param decimals the decimals to use for each pooled token, * eg 8 for WBTC. Cannot be larger than POOL_PRECISION_DECIMALS * @param lpTokenName the long-form name of the token to be deployed * @param lpTokenSymbol the short symbol for the token to be deployed * @param _a the amplification coefficient * n * (n - 1). See the * StableSwap paper for details * @param _fee default swap fee to be initialized with * @param _adminFee default adminFee to be initialized with * @param lpTokenTargetAddress the address of an existing LPToken contract to use as a target */ function initialize( IERC20[] memory _pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 _a, uint256 _fee, uint256 _adminFee, address lpTokenTargetAddress ) public virtual initializer { __OwnerPausable_init(); __ReentrancyGuard_init(); // Check _pooledTokens and precisions parameter require(_pooledTokens.length > 1, "_pooledTokens.length <= 1"); require(_pooledTokens.length <= 32, "_pooledTokens.length > 32"); require( _pooledTokens.length == decimals.length, "_pooledTokens decimals mismatch" ); uint256[] memory precisionMultipliers = new uint256[](decimals.length); for (uint8 i = 0; i < _pooledTokens.length; i++) { if (i > 0) { // Check if index is already used. Check if 0th element is a duplicate. require( tokenIndexes[address(_pooledTokens[i])] == 0 && _pooledTokens[0] != _pooledTokens[i], "Duplicate tokens" ); } require( address(_pooledTokens[i]) != address(0), "The 0 address isn't an ERC-20" ); require( decimals[i] <= SwapUtils.POOL_PRECISION_DECIMALS, "Token decimals exceeds max" ); precisionMultipliers[i] = 10 ** uint256(SwapUtils.POOL_PRECISION_DECIMALS).sub( uint256(decimals[i]) ); tokenIndexes[address(_pooledTokens[i])] = i; } // Check _a, _fee, _adminFee, _withdrawFee parameters require(_a < AmplificationUtils.MAX_A, "_a exceeds maximum"); require(_fee < SwapUtils.MAX_SWAP_FEE, "_fee exceeds maximum"); require( _adminFee < SwapUtils.MAX_ADMIN_FEE, "_adminFee exceeds maximum" ); // Clone and initialize a LPToken contract LPToken lpToken = LPToken(Clones.clone(lpTokenTargetAddress)); require( lpToken.initialize(lpTokenName, lpTokenSymbol), "could not init lpToken clone" ); // Initialize swapStorage struct swapStorage.lpToken = lpToken; swapStorage.pooledTokens = _pooledTokens; swapStorage.tokenPrecisionMultipliers = precisionMultipliers; swapStorage.balances = new uint256[](_pooledTokens.length); swapStorage.initialA = _a.mul(AmplificationUtils.A_PRECISION); swapStorage.futureA = _a.mul(AmplificationUtils.A_PRECISION); // swapStorage.initialATime = 0; // swapStorage.futureATime = 0; swapStorage.swapFee = _fee; swapStorage.adminFee = _adminFee; } /*** MODIFIERS ***/ /** * @notice Modifier to check deadline against current timestamp * @param deadline latest timestamp to accept this transaction */ modifier deadlineCheck(uint256 deadline) { require(block.timestamp <= deadline, "Deadline not met"); _; } /*** VIEW FUNCTIONS ***/ /** * @notice Return A, the amplification coefficient * n * (n - 1) * @dev See the StableSwap paper for details * @return A parameter */ function getA() external view virtual returns (uint256) { return swapStorage.getA(); } /** * @notice Return A in its raw precision form * @dev See the StableSwap paper for details * @return A parameter in its raw precision form */ function getAPrecise() external view virtual returns (uint256) { return swapStorage.getAPrecise(); } /** * @notice Return address of the pooled token at given index. Reverts if tokenIndex is out of range. * @param index the index of the token * @return address of the token at given index */ function getToken(uint8 index) public view virtual returns (IERC20) { require(index < swapStorage.pooledTokens.length, "Out of range"); return swapStorage.pooledTokens[index]; } /** * @notice Return the index of the given token address. Reverts if no matching * token is found. * @param tokenAddress address of the token * @return the index of the given token address */ function getTokenIndex(address tokenAddress) public view virtual returns (uint8) { uint8 index = tokenIndexes[tokenAddress]; require( address(getToken(index)) == tokenAddress, "Token does not exist" ); return index; } /** * @notice Return current balance of the pooled token at given index * @param index the index of the token * @return current balance of the pooled token at given index with token's native precision */ function getTokenBalance(uint8 index) external view virtual returns (uint256) { require(index < swapStorage.pooledTokens.length, "Index out of range"); return swapStorage.balances[index]; } /** * @notice Get the virtual price, to help calculate profit * @return the virtual price, scaled to the POOL_PRECISION_DECIMALS */ function getVirtualPrice() external view virtual returns (uint256) { return swapStorage.getVirtualPrice(); } /** * @notice Calculate amount of tokens you receive on swap * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell. If the token charges * a fee on transfers, use the amount that gets transferred after the fee. * @return amount of tokens the user will receive */ function calculateSwap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view virtual returns (uint256) { return swapStorage.calculateSwap(tokenIndexFrom, tokenIndexTo, dx); } /** * @notice A simple method to calculate prices from deposits or * withdrawals, excluding fees but including slippage. This is * helpful as an input into the various "min" parameters on calls * to fight front-running * * @dev This shouldn't be used outside frontends for user estimates. * * @param amounts an array of token amounts to deposit or withdrawal, * corresponding to pooledTokens. The amount should be in each * pooled token's native precision. If a token charges a fee on transfers, * use the amount that gets transferred after the fee. * @param deposit whether this is a deposit or a withdrawal * @return token amount the user will receive */ function calculateTokenAmount(uint256[] calldata amounts, bool deposit) external view virtual returns (uint256) { return swapStorage.calculateTokenAmount(amounts, deposit); } /** * @notice A simple method to calculate amount of each underlying * tokens that is returned upon burning given amount of LP tokens * @param amount the amount of LP tokens that would be burned on withdrawal * @return array of token balances that the user will receive */ function calculateRemoveLiquidity(uint256 amount) external view virtual returns (uint256[] memory) { return swapStorage.calculateRemoveLiquidity(amount); } /** * @notice Calculate the amount of underlying token available to withdraw * when withdrawing via only single token * @param tokenAmount the amount of LP token to burn * @param tokenIndex index of which token will be withdrawn * @return availableTokenAmount calculated amount of underlying token * available to withdraw */ function calculateRemoveLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex ) external view virtual returns (uint256 availableTokenAmount) { return swapStorage.calculateWithdrawOneToken(tokenAmount, tokenIndex); } /** * @notice This function reads the accumulated amount of admin fees of the token with given index * @param index Index of the pooled token * @return admin's token balance in the token's precision */ function getAdminBalance(uint256 index) external view virtual returns (uint256) { return swapStorage.getAdminBalance(index); } /*** STATE MODIFYING FUNCTIONS ***/ /** * @notice Swap two tokens using this pool * @param tokenIndexFrom the token the user wants to swap from * @param tokenIndexTo the token the user wants to swap to * @param dx the amount of tokens the user wants to swap from * @param minDy the min amount the user would like to receive, or revert. * @param deadline latest timestamp to accept this transaction */ function swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external virtual nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.swap(tokenIndexFrom, tokenIndexTo, dx, minDy); } /** * @notice Add liquidity to the pool with the given amounts of tokens * @param amounts the amounts of each token to add, in their native precision * @param minToMint the minimum LP tokens adding this amount of liquidity * should mint, otherwise revert. Handy for front-running mitigation * @param deadline latest timestamp to accept this transaction * @return amount of LP token user minted and received */ function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline ) external virtual nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.addLiquidity(amounts, minToMint); } /** * @notice Burn LP tokens to remove liquidity from the pool. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @dev Liquidity can always be removed, even when the pool is paused. * @param amount the amount of LP tokens to burn * @param minAmounts the minimum amounts of each token in the pool * acceptable for this burn. Useful as a front-running mitigation * @param deadline latest timestamp to accept this transaction * @return amounts of tokens user received */ function removeLiquidity( uint256 amount, uint256[] calldata minAmounts, uint256 deadline ) external virtual nonReentrant deadlineCheck(deadline) returns (uint256[] memory) { return swapStorage.removeLiquidity(amount, minAmounts); } /** * @notice Remove liquidity from the pool all in one token. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @param tokenAmount the amount of the token you want to receive * @param tokenIndex the index of the token you want to receive * @param minAmount the minimum amount to withdraw, otherwise revert * @param deadline latest timestamp to accept this transaction * @return amount of chosen token user received */ function removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline ) external virtual nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.removeLiquidityOneToken( tokenAmount, tokenIndex, minAmount ); } /** * @notice Remove liquidity from the pool, weighted differently than the * pool's current balances. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @param amounts how much of each token to withdraw * @param maxBurnAmount the max LP token provider is willing to pay to * remove liquidity. Useful as a front-running mitigation. * @param deadline latest timestamp to accept this transaction * @return amount of LP tokens burned */ function removeLiquidityImbalance( uint256[] calldata amounts, uint256 maxBurnAmount, uint256 deadline ) external virtual nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.removeLiquidityImbalance(amounts, maxBurnAmount); } /*** ADMIN FUNCTIONS ***/ /** * @notice Withdraw all admin fees to the contract owner */ function withdrawAdminFees() external onlyOwner { swapStorage.withdrawAdminFees(owner()); } /** * @notice Update the admin fee. Admin fee takes portion of the swap fee. * @param newAdminFee new admin fee to be applied on future transactions */ function setAdminFee(uint256 newAdminFee) external onlyOwner { swapStorage.setAdminFee(newAdminFee); } /** * @notice Update the swap fee to be applied on swaps * @param newSwapFee new swap fee to be applied on future transactions */ function setSwapFee(uint256 newSwapFee) external onlyOwner { swapStorage.setSwapFee(newSwapFee); } /** * @notice Start ramping up or down A parameter towards given futureA and futureTime * Checks if the change is too rapid, and commits the new A value only when it falls under * the limit range. * @param futureA the new A to ramp towards * @param futureTime timestamp when the new A should be reached */ function rampA(uint256 futureA, uint256 futureTime) external onlyOwner { swapStorage.rampA(futureA, futureTime); } /** * @notice Stop ramping A immediately. Reverts if ramp A is already stopped. */ function stopRampA() external onlyOwner { swapStorage.stopRampA(); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for * deploying minimal proxy contracts, also known as "clones". * * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies * > a minimal bytecode implementation that delegates all calls to a known, fixed address. * * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2` * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the * deterministic method. * * _Available since v3.4._ */ library Clones { /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `master`. * * This function uses the create opcode, which should never revert. */ function clone(address master) internal returns (address instance) { // solhint-disable-next-line no-inline-assembly assembly { let ptr := mload(0x40) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) mstore(add(ptr, 0x14), shl(0x60, master)) mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) instance := create(0, ptr, 0x37) } require(instance != address(0), "ERC1167: create failed"); } /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `master`. * * This function uses the create2 opcode and a `salt` to deterministically deploy * the clone. Using the same `master` and `salt` multiple time will revert, since * the clones cannot be deployed twice at the same address. */ function cloneDeterministic(address master, bytes32 salt) internal returns (address instance) { // solhint-disable-next-line no-inline-assembly assembly { let ptr := mload(0x40) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) mstore(add(ptr, 0x14), shl(0x60, master)) mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) instance := create2(0, ptr, 0x37, salt) } require(instance != address(0), "ERC1167: create2 failed"); } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress(address master, bytes32 salt, address deployer) internal pure returns (address predicted) { // solhint-disable-next-line no-inline-assembly assembly { let ptr := mload(0x40) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) mstore(add(ptr, 0x14), shl(0x60, master)) mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000) mstore(add(ptr, 0x38), shl(0x60, deployer)) mstore(add(ptr, 0x4c), salt) mstore(add(ptr, 0x6c), keccak256(ptr, 0x37)) predicted := keccak256(add(ptr, 0x37), 0x55) } } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress(address master, bytes32 salt) internal view returns (address predicted) { return predictDeterministicAddress(master, salt, address(this)); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../proxy/Initializable.sol"; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuardUpgradeable is Initializable { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; function __ReentrancyGuard_init() internal initializer { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal initializer { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } uint256[49] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; /** * @title OwnerPausable * @notice An ownable contract allows the owner to pause and unpause the * contract without a delay. * @dev Only methods using the provided modifiers will be paused. */ abstract contract OwnerPausableUpgradeable is OwnableUpgradeable, PausableUpgradeable { function __OwnerPausable_init() internal initializer { __Context_init_unchained(); __Ownable_init_unchained(); __Pausable_init_unchained(); } /** * @notice Pause the contract. Revert if already paused. */ function pause() external onlyOwner { PausableUpgradeable._pause(); } /** * @notice Unpause the contract. Revert if already unpaused. */ function unpause() external onlyOwner { PausableUpgradeable._unpause(); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "./ContextUpgradeable.sol"; import "../proxy/Initializable.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract PausableUpgradeable is Initializable, ContextUpgradeable { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ function __Pausable_init() internal initializer { __Context_init_unchained(); __Pausable_init_unchained(); } function __Pausable_init_unchained() internal initializer { _paused = false; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { require(!paused(), "Pausable: paused"); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { require(paused(), "Pausable: not paused"); _; } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } uint256[49] private __gap; }
// SPDX-License-Identifier: MIT WITH AGPL-3.0-only pragma solidity 0.6.12; import "./Swap.sol"; import "./interfaces/IFlashLoanReceiver.sol"; /** * @title Swap - A StableSwap implementation in solidity. * @notice This contract is responsible for custody of closely pegged assets (eg. group of stablecoins) * and automatic market making system. Users become an LP (Liquidity Provider) by depositing their tokens * in desired ratios for an exchange of the pool token that represents their share of the pool. * Users can burn pool tokens and withdraw their share of token(s). * * Each time a swap between the pooled tokens happens, a set fee incurs which effectively gets * distributed to the LPs. * * In case of emergencies, admin can pause additional deposits, swaps, or single-asset withdraws - which * stops the ratio of the tokens in the pool from changing. * Users can always withdraw their tokens via multi-asset withdraws. * * @dev Most of the logic is stored as a library `SwapUtils` for the sake of reducing contract's * deployment size. */ contract SwapFlashLoan is Swap { // Total fee that is charged on all flashloans in BPS. Borrowers must repay the amount plus the flash loan fee. // This fee is split between the protocol and the pool. uint256 public flashLoanFeeBPS; // Share of the flash loan fee that goes to the protocol in BPS. A portion of each flash loan fee is allocated // to the protocol rather than the pool. uint256 public protocolFeeShareBPS; // Max BPS for limiting flash loan fee settings. uint256 public constant MAX_BPS = 10000; /*** EVENTS ***/ event FlashLoan( address indexed receiver, uint8 tokenIndex, uint256 amount, uint256 amountFee, uint256 protocolFee ); /** * @notice Initializes this Swap contract with the given parameters. * This will also clone a LPToken contract that represents users' * LP positions. The owner of LPToken will be this contract - which means * only this contract is allowed to mint/burn tokens. * * @param _pooledTokens an array of ERC20s this pool will accept * @param decimals the decimals to use for each pooled token, * eg 8 for WBTC. Cannot be larger than POOL_PRECISION_DECIMALS * @param lpTokenName the long-form name of the token to be deployed * @param lpTokenSymbol the short symbol for the token to be deployed * @param _a the amplification coefficient * n * (n - 1). See the * StableSwap paper for details * @param _fee default swap fee to be initialized with * @param _adminFee default adminFee to be initialized with * @param lpTokenTargetAddress the address of an existing LPToken contract to use as a target */ function initialize( IERC20[] memory _pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 _a, uint256 _fee, uint256 _adminFee, address lpTokenTargetAddress ) public virtual override initializer { Swap.initialize( _pooledTokens, decimals, lpTokenName, lpTokenSymbol, _a, _fee, _adminFee, lpTokenTargetAddress ); flashLoanFeeBPS = 8; // 8 bps protocolFeeShareBPS = 0; // 0 bps } /*** STATE MODIFYING FUNCTIONS ***/ /** * @notice Borrow the specified token from this pool for this transaction only. This function will call * `IFlashLoanReceiver(receiver).executeOperation` and the `receiver` must return the full amount of the token * and the associated fee by the end of the callback transaction. If the conditions are not met, this call * is reverted. * @param receiver the address of the receiver of the token. This address must implement the IFlashLoanReceiver * interface and the callback function `executeOperation`. * @param token the protocol fee in bps to be applied on the total flash loan fee * @param amount the total amount to borrow in this transaction * @param params optional data to pass along to the callback function */ function flashLoan( address receiver, IERC20 token, uint256 amount, bytes memory params ) external nonReentrant { uint8 tokenIndex = getTokenIndex(address(token)); uint256 availableLiquidityBefore = token.balanceOf(address(this)); uint256 protocolBalanceBefore = availableLiquidityBefore.sub( swapStorage.balances[tokenIndex] ); require( amount > 0 && availableLiquidityBefore >= amount, "invalid amount" ); // Calculate the additional amount of tokens the pool should end up with uint256 amountFee = amount.mul(flashLoanFeeBPS).div(10000); // Calculate the portion of the fee that will go to the protocol uint256 protocolFee = amountFee.mul(protocolFeeShareBPS).div(10000); require(amountFee > 0, "amount is small for a flashLoan"); // Transfer the requested amount of tokens token.safeTransfer(receiver, amount); // Execute callback function on receiver IFlashLoanReceiver(receiver).executeOperation( address(this), address(token), amount, amountFee, params ); uint256 availableLiquidityAfter = token.balanceOf(address(this)); require( availableLiquidityAfter >= availableLiquidityBefore.add(amountFee), "flashLoan fee is not met" ); swapStorage.balances[tokenIndex] = availableLiquidityAfter .sub(protocolBalanceBefore) .sub(protocolFee); emit FlashLoan(receiver, tokenIndex, amount, amountFee, protocolFee); } /*** ADMIN FUNCTIONS ***/ /** * @notice Updates the flash loan fee parameters. This function can only be called by the owner. * @param newFlashLoanFeeBPS the total fee in bps to be applied on future flash loans * @param newProtocolFeeShareBPS the protocol fee in bps to be applied on the total flash loan fee */ function setFlashLoanFees( uint256 newFlashLoanFeeBPS, uint256 newProtocolFeeShareBPS ) external onlyOwner { require( newFlashLoanFeeBPS > 0 && newFlashLoanFeeBPS <= MAX_BPS && newProtocolFeeShareBPS <= MAX_BPS, "fees are not in valid range" ); flashLoanFeeBPS = newFlashLoanFeeBPS; protocolFeeShareBPS = newProtocolFeeShareBPS; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.6.12; /** * @title IFlashLoanReceiver interface * @notice Interface for the Saddle fee IFlashLoanReceiver. Modified from Aave's IFlashLoanReceiver interface. * https://github.com/aave/aave-protocol/blob/4b4545fb583fd4f400507b10f3c3114f45b8a037/contracts/flashloan/interfaces/IFlashLoanReceiver.sol * @author Aave * @dev implement this interface to develop a flashloan-compatible flashLoanReceiver contract **/ interface IFlashLoanReceiver { function executeOperation( address pool, address token, uint256 amount, uint256 fee, bytes calldata params ) external; }
// SPDX-License-Identifier: MIT WITH AGPL-3.0-only pragma solidity 0.6.12; import "./SwapV1.sol"; import "./interfaces/IFlashLoanReceiver.sol"; /** * @title Swap - A StableSwap implementation in solidity. * @notice This contract is responsible for custody of closely pegged assets (eg. group of stablecoins) * and automatic market making system. Users become an LP (Liquidity Provider) by depositing their tokens * in desired ratios for an exchange of the pool token that represents their share of the pool. * Users can burn pool tokens and withdraw their share of token(s). * * Each time a swap between the pooled tokens happens, a set fee incurs which effectively gets * distributed to the LPs. * * In case of emergencies, admin can pause additional deposits, swaps, or single-asset withdraws - which * stops the ratio of the tokens in the pool from changing. * Users can always withdraw their tokens via multi-asset withdraws. * * @dev Most of the logic is stored as a library `SwapUtils` for the sake of reducing contract's * deployment size. */ contract SwapFlashLoanV1 is SwapV1 { // Total fee that is charged on all flashloans in BPS. Borrowers must repay the amount plus the flash loan fee. // This fee is split between the protocol and the pool. uint256 public flashLoanFeeBPS; // Share of the flash loan fee that goes to the protocol in BPS. A portion of each flash loan fee is allocated // to the protocol rather than the pool. uint256 public protocolFeeShareBPS; // Max BPS for limiting flash loan fee settings. uint256 public constant MAX_BPS = 10000; /*** EVENTS ***/ event FlashLoan( address indexed receiver, uint8 tokenIndex, uint256 amount, uint256 amountFee, uint256 protocolFee ); /** * @notice Initializes this Swap contract with the given parameters. * This will also clone a LPToken contract that represents users' * LP positions. The owner of LPToken will be this contract - which means * only this contract is allowed to mint/burn tokens. * * @param _pooledTokens an array of ERC20s this pool will accept * @param decimals the decimals to use for each pooled token, * eg 8 for WBTC. Cannot be larger than POOL_PRECISION_DECIMALS * @param lpTokenName the long-form name of the token to be deployed * @param lpTokenSymbol the short symbol for the token to be deployed * @param _a the amplification coefficient * n * (n - 1). See the * StableSwap paper for details * @param _fee default swap fee to be initialized with * @param _adminFee default adminFee to be initialized with * @param _withdrawFee default withdrawFee to be initialized with * @param lpTokenTargetAddress the address of an existing LPToken contract to use as a target */ function initialize( IERC20[] memory _pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 _a, uint256 _fee, uint256 _adminFee, uint256 _withdrawFee, address lpTokenTargetAddress ) public virtual override initializer { SwapV1.initialize( _pooledTokens, decimals, lpTokenName, lpTokenSymbol, _a, _fee, _adminFee, _withdrawFee, lpTokenTargetAddress ); flashLoanFeeBPS = 8; // 8 bps protocolFeeShareBPS = 0; // 0 bps } /*** STATE MODIFYING FUNCTIONS ***/ /** * @notice Borrow the specified token from this pool for this transaction only. This function will call * `IFlashLoanReceiver(receiver).executeOperation` and the `receiver` must return the full amount of the token * and the associated fee by the end of the callback transaction. If the conditions are not met, this call * is reverted. * @param receiver the address of the receiver of the token. This address must implement the IFlashLoanReceiver * interface and the callback function `executeOperation`. * @param token the protocol fee in bps to be applied on the total flash loan fee * @param amount the total amount to borrow in this transaction * @param params optional data to pass along to the callback function */ function flashLoan( address receiver, IERC20 token, uint256 amount, bytes memory params ) external nonReentrant { uint8 tokenIndex = getTokenIndex(address(token)); uint256 availableLiquidityBefore = token.balanceOf(address(this)); uint256 protocolBalanceBefore = availableLiquidityBefore.sub( swapStorage.balances[tokenIndex] ); require( amount > 0 && availableLiquidityBefore >= amount, "invalid amount" ); // Calculate the additional amount of tokens the pool should end up with uint256 amountFee = amount.mul(flashLoanFeeBPS).div(10000); // Calculate the portion of the fee that will go to the protocol uint256 protocolFee = amountFee.mul(protocolFeeShareBPS).div(10000); require(amountFee > 0, "amount is small for a flashLoan"); // Transfer the requested amount of tokens token.safeTransfer(receiver, amount); // Execute callback function on receiver IFlashLoanReceiver(receiver).executeOperation( address(this), address(token), amount, amountFee, params ); uint256 availableLiquidityAfter = token.balanceOf(address(this)); require( availableLiquidityAfter >= availableLiquidityBefore.add(amountFee), "flashLoan fee is not met" ); swapStorage.balances[tokenIndex] = availableLiquidityAfter .sub(protocolBalanceBefore) .sub(protocolFee); emit FlashLoan(receiver, tokenIndex, amount, amountFee, protocolFee); } /*** ADMIN FUNCTIONS ***/ /** * @notice Updates the flash loan fee parameters. This function can only be called by the owner. * @param newFlashLoanFeeBPS the total fee in bps to be applied on future flash loans * @param newProtocolFeeShareBPS the protocol fee in bps to be applied on the total flash loan fee */ function setFlashLoanFees( uint256 newFlashLoanFeeBPS, uint256 newProtocolFeeShareBPS ) external onlyOwner { require( newFlashLoanFeeBPS > 0 && newFlashLoanFeeBPS <= MAX_BPS && newProtocolFeeShareBPS <= MAX_BPS, "fees are not in valid range" ); flashLoanFeeBPS = newFlashLoanFeeBPS; protocolFeeShareBPS = newProtocolFeeShareBPS; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts/proxy/Clones.sol"; import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import "./OwnerPausableUpgradeable.sol"; import "./SwapUtilsV1.sol"; import "./AmplificationUtilsV1.sol"; /** * @title Swap - A StableSwap implementation in solidity. * @notice This contract is responsible for custody of closely pegged assets (eg. group of stablecoins) * and automatic market making system. Users become an LP (Liquidity Provider) by depositing their tokens * in desired ratios for an exchange of the pool token that represents their share of the pool. * Users can burn pool tokens and withdraw their share of token(s). * * Each time a swap between the pooled tokens happens, a set fee incurs which effectively gets * distributed to the LPs. * * In case of emergencies, admin can pause additional deposits, swaps, or single-asset withdraws - which * stops the ratio of the tokens in the pool from changing. * Users can always withdraw their tokens via multi-asset withdraws. * * @dev Most of the logic is stored as a library `SwapUtils` for the sake of reducing contract's * deployment size. */ contract SwapV1 is OwnerPausableUpgradeable, ReentrancyGuardUpgradeable { using SafeERC20 for IERC20; using SafeMath for uint256; using SwapUtilsV1 for SwapUtilsV1.Swap; using AmplificationUtilsV1 for SwapUtilsV1.Swap; // Struct storing data responsible for automatic market maker functionalities. In order to // access this data, this contract uses SwapUtils library. For more details, see SwapUtilsV1.sol SwapUtilsV1.Swap public swapStorage; // Maps token address to an index in the pool. Used to prevent duplicate tokens in the pool. // getTokenIndex function also relies on this mapping to retrieve token index. mapping(address => uint8) private tokenIndexes; /*** EVENTS ***/ // events replicated from SwapUtils to make the ABI easier for dumb // clients event TokenSwap( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); event AddLiquidity( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event RemoveLiquidity( address indexed provider, uint256[] tokenAmounts, uint256 lpTokenSupply ); event RemoveLiquidityOne( address indexed provider, uint256 lpTokenAmount, uint256 lpTokenSupply, uint256 boughtId, uint256 tokensBought ); event RemoveLiquidityImbalance( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event NewAdminFee(uint256 newAdminFee); event NewSwapFee(uint256 newSwapFee); event NewWithdrawFee(uint256 newWithdrawFee); event RampA( uint256 oldA, uint256 newA, uint256 initialTime, uint256 futureTime ); event StopRampA(uint256 currentA, uint256 time); /** * @notice Initializes this Swap contract with the given parameters. * This will also clone a LPToken contract that represents users' * LP positions. The owner of LPToken will be this contract - which means * only this contract is allowed to mint/burn tokens. * * @param _pooledTokens an array of ERC20s this pool will accept * @param decimals the decimals to use for each pooled token, * eg 8 for WBTC. Cannot be larger than POOL_PRECISION_DECIMALS * @param lpTokenName the long-form name of the token to be deployed * @param lpTokenSymbol the short symbol for the token to be deployed * @param _a the amplification coefficient * n * (n - 1). See the * StableSwap paper for details * @param _fee default swap fee to be initialized with * @param _adminFee default adminFee to be initialized with * @param _withdrawFee default withdrawFee to be initialized with * @param lpTokenTargetAddress the address of an existing LPToken contract to use as a target */ function initialize( IERC20[] memory _pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 _a, uint256 _fee, uint256 _adminFee, uint256 _withdrawFee, address lpTokenTargetAddress ) public virtual initializer { __OwnerPausable_init(); __ReentrancyGuard_init(); // Check _pooledTokens and precisions parameter require(_pooledTokens.length > 1, "_pooledTokens.length <= 1"); require(_pooledTokens.length <= 32, "_pooledTokens.length > 32"); require( _pooledTokens.length == decimals.length, "_pooledTokens decimals mismatch" ); uint256[] memory precisionMultipliers = new uint256[](decimals.length); for (uint8 i = 0; i < _pooledTokens.length; i++) { if (i > 0) { // Check if index is already used. Check if 0th element is a duplicate. require( tokenIndexes[address(_pooledTokens[i])] == 0 && _pooledTokens[0] != _pooledTokens[i], "Duplicate tokens" ); } require( address(_pooledTokens[i]) != address(0), "The 0 address isn't an ERC-20" ); require( decimals[i] <= SwapUtilsV1.POOL_PRECISION_DECIMALS, "Token decimals exceeds max" ); precisionMultipliers[i] = 10 ** uint256(SwapUtilsV1.POOL_PRECISION_DECIMALS).sub( uint256(decimals[i]) ); tokenIndexes[address(_pooledTokens[i])] = i; } // Check _a, _fee, _adminFee, _withdrawFee parameters require(_a < AmplificationUtilsV1.MAX_A, "_a exceeds maximum"); require(_fee < SwapUtilsV1.MAX_SWAP_FEE, "_fee exceeds maximum"); require( _adminFee < SwapUtilsV1.MAX_ADMIN_FEE, "_adminFee exceeds maximum" ); require( _withdrawFee < SwapUtilsV1.MAX_WITHDRAW_FEE, "_withdrawFee exceeds maximum" ); // Clone and initialize a LPToken contract LPToken lpToken = LPToken(Clones.clone(lpTokenTargetAddress)); require( lpToken.initialize(lpTokenName, lpTokenSymbol), "could not init lpToken clone" ); // Initialize swapStorage struct swapStorage.lpToken = lpToken; swapStorage.pooledTokens = _pooledTokens; swapStorage.tokenPrecisionMultipliers = precisionMultipliers; swapStorage.balances = new uint256[](_pooledTokens.length); swapStorage.initialA = _a.mul(AmplificationUtilsV1.A_PRECISION); swapStorage.futureA = _a.mul(AmplificationUtilsV1.A_PRECISION); // swapStorage.initialATime = 0; // swapStorage.futureATime = 0; swapStorage.swapFee = _fee; swapStorage.adminFee = _adminFee; swapStorage.defaultWithdrawFee = _withdrawFee; } /*** MODIFIERS ***/ /** * @notice Modifier to check deadline against current timestamp * @param deadline latest timestamp to accept this transaction */ modifier deadlineCheck(uint256 deadline) { require(block.timestamp <= deadline, "Deadline not met"); _; } /*** VIEW FUNCTIONS ***/ /** * @notice Return A, the amplification coefficient * n * (n - 1) * @dev See the StableSwap paper for details * @return A parameter */ function getA() external view virtual returns (uint256) { return swapStorage.getA(); } /** * @notice Return A in its raw precision form * @dev See the StableSwap paper for details * @return A parameter in its raw precision form */ function getAPrecise() external view virtual returns (uint256) { return swapStorage.getAPrecise(); } /** * @notice Return address of the pooled token at given index. Reverts if tokenIndex is out of range. * @param index the index of the token * @return address of the token at given index */ function getToken(uint8 index) public view virtual returns (IERC20) { require(index < swapStorage.pooledTokens.length, "Out of range"); return swapStorage.pooledTokens[index]; } /** * @notice Return the index of the given token address. Reverts if no matching * token is found. * @param tokenAddress address of the token * @return the index of the given token address */ function getTokenIndex(address tokenAddress) public view virtual returns (uint8) { uint8 index = tokenIndexes[tokenAddress]; require( address(getToken(index)) == tokenAddress, "Token does not exist" ); return index; } /** * @notice Return timestamp of last deposit of given address * @return timestamp of the last deposit made by the given address */ function getDepositTimestamp(address user) external view virtual returns (uint256) { return swapStorage.getDepositTimestamp(user); } /** * @notice Return current balance of the pooled token at given index * @param index the index of the token * @return current balance of the pooled token at given index with token's native precision */ function getTokenBalance(uint8 index) external view virtual returns (uint256) { require(index < swapStorage.pooledTokens.length, "Index out of range"); return swapStorage.balances[index]; } /** * @notice Get the virtual price, to help calculate profit * @return the virtual price, scaled to the POOL_PRECISION_DECIMALS */ function getVirtualPrice() external view virtual returns (uint256) { return swapStorage.getVirtualPrice(); } /** * @notice Calculate amount of tokens you receive on swap * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell. If the token charges * a fee on transfers, use the amount that gets transferred after the fee. * @return amount of tokens the user will receive */ function calculateSwap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view virtual returns (uint256) { return swapStorage.calculateSwap(tokenIndexFrom, tokenIndexTo, dx); } /** * @notice A simple method to calculate prices from deposits or * withdrawals, excluding fees but including slippage. This is * helpful as an input into the various "min" parameters on calls * to fight front-running * * @dev This shouldn't be used outside frontends for user estimates. * * @param account address that is depositing or withdrawing tokens * @param amounts an array of token amounts to deposit or withdrawal, * corresponding to pooledTokens. The amount should be in each * pooled token's native precision. If a token charges a fee on transfers, * use the amount that gets transferred after the fee. * @param deposit whether this is a deposit or a withdrawal * @return token amount the user will receive */ function calculateTokenAmount( address account, uint256[] calldata amounts, bool deposit ) external view virtual returns (uint256) { return swapStorage.calculateTokenAmount(account, amounts, deposit); } /** * @notice A simple method to calculate amount of each underlying * tokens that is returned upon burning given amount of LP tokens * @param account the address that is withdrawing tokens * @param amount the amount of LP tokens that would be burned on withdrawal * @return array of token balances that the user will receive */ function calculateRemoveLiquidity(address account, uint256 amount) external view virtual returns (uint256[] memory) { return swapStorage.calculateRemoveLiquidity(account, amount); } /** * @notice Calculate the amount of underlying token available to withdraw * when withdrawing via only single token * @param account the address that is withdrawing tokens * @param tokenAmount the amount of LP token to burn * @param tokenIndex index of which token will be withdrawn * @return availableTokenAmount calculated amount of underlying token * available to withdraw */ function calculateRemoveLiquidityOneToken( address account, uint256 tokenAmount, uint8 tokenIndex ) external view virtual returns (uint256 availableTokenAmount) { return swapStorage.calculateWithdrawOneToken( account, tokenAmount, tokenIndex ); } /** * @notice Calculate the fee that is applied when the given user withdraws. The withdraw fee * decays linearly over period of 4 weeks. For example, depositing and withdrawing right away * will charge you the full amount of withdraw fee. But withdrawing after 4 weeks will charge you * no additional fees. * @dev returned value should be divided by FEE_DENOMINATOR to convert to correct decimals * @param user address you want to calculate withdraw fee of * @return current withdraw fee of the user */ function calculateCurrentWithdrawFee(address user) external view virtual returns (uint256) { return swapStorage.calculateCurrentWithdrawFee(user); } /** * @notice This function reads the accumulated amount of admin fees of the token with given index * @param index Index of the pooled token * @return admin's token balance in the token's precision */ function getAdminBalance(uint256 index) external view virtual returns (uint256) { return swapStorage.getAdminBalance(index); } /*** STATE MODIFYING FUNCTIONS ***/ /** * @notice Swap two tokens using this pool * @param tokenIndexFrom the token the user wants to swap from * @param tokenIndexTo the token the user wants to swap to * @param dx the amount of tokens the user wants to swap from * @param minDy the min amount the user would like to receive, or revert. * @param deadline latest timestamp to accept this transaction */ function swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external virtual nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.swap(tokenIndexFrom, tokenIndexTo, dx, minDy); } /** * @notice Add liquidity to the pool with the given amounts of tokens * @param amounts the amounts of each token to add, in their native precision * @param minToMint the minimum LP tokens adding this amount of liquidity * should mint, otherwise revert. Handy for front-running mitigation * @param deadline latest timestamp to accept this transaction * @return amount of LP token user minted and received */ function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline ) external virtual nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.addLiquidity(amounts, minToMint); } /** * @notice Burn LP tokens to remove liquidity from the pool. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @dev Liquidity can always be removed, even when the pool is paused. * @param amount the amount of LP tokens to burn * @param minAmounts the minimum amounts of each token in the pool * acceptable for this burn. Useful as a front-running mitigation * @param deadline latest timestamp to accept this transaction * @return amounts of tokens user received */ function removeLiquidity( uint256 amount, uint256[] calldata minAmounts, uint256 deadline ) external virtual nonReentrant deadlineCheck(deadline) returns (uint256[] memory) { return swapStorage.removeLiquidity(amount, minAmounts); } /** * @notice Remove liquidity from the pool all in one token. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @param tokenAmount the amount of the token you want to receive * @param tokenIndex the index of the token you want to receive * @param minAmount the minimum amount to withdraw, otherwise revert * @param deadline latest timestamp to accept this transaction * @return amount of chosen token user received */ function removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline ) external virtual nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.removeLiquidityOneToken( tokenAmount, tokenIndex, minAmount ); } /** * @notice Remove liquidity from the pool, weighted differently than the * pool's current balances. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @param amounts how much of each token to withdraw * @param maxBurnAmount the max LP token provider is willing to pay to * remove liquidity. Useful as a front-running mitigation. * @param deadline latest timestamp to accept this transaction * @return amount of LP tokens burned */ function removeLiquidityImbalance( uint256[] calldata amounts, uint256 maxBurnAmount, uint256 deadline ) external virtual nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.removeLiquidityImbalance(amounts, maxBurnAmount); } /*** ADMIN FUNCTIONS ***/ /** * @notice Updates the user withdraw fee. This function can only be called by * the pool token. Should be used to update the withdraw fee on transfer of pool tokens. * Transferring your pool token will reset the 4 weeks period. If the recipient is already * holding some pool tokens, the withdraw fee will be discounted in respective amounts. * @param recipient address of the recipient of pool token * @param transferAmount amount of pool token to transfer */ function updateUserWithdrawFee(address recipient, uint256 transferAmount) external { require( msg.sender == address(swapStorage.lpToken), "Only callable by pool token" ); swapStorage.updateUserWithdrawFee(recipient, transferAmount); } /** * @notice Withdraw all admin fees to the contract owner */ function withdrawAdminFees() external onlyOwner { swapStorage.withdrawAdminFees(owner()); } /** * @notice Update the admin fee. Admin fee takes portion of the swap fee. * @param newAdminFee new admin fee to be applied on future transactions */ function setAdminFee(uint256 newAdminFee) external onlyOwner { swapStorage.setAdminFee(newAdminFee); } /** * @notice Update the swap fee to be applied on swaps * @param newSwapFee new swap fee to be applied on future transactions */ function setSwapFee(uint256 newSwapFee) external onlyOwner { swapStorage.setSwapFee(newSwapFee); } /** * @notice Update the withdraw fee. This fee decays linearly over 4 weeks since * user's last deposit. * @param newWithdrawFee new withdraw fee to be applied on future deposits */ function setDefaultWithdrawFee(uint256 newWithdrawFee) external onlyOwner { swapStorage.setDefaultWithdrawFee(newWithdrawFee); } /** * @notice Start ramping up or down A parameter towards given futureA and futureTime * Checks if the change is too rapid, and commits the new A value only when it falls under * the limit range. * @param futureA the new A to ramp towards * @param futureTime timestamp when the new A should be reached */ function rampA(uint256 futureA, uint256 futureTime) external onlyOwner { swapStorage.rampA(futureA, futureTime); } /** * @notice Stop ramping A immediately. Reverts if ramp A is already stopped. */ function stopRampA() external onlyOwner { swapStorage.stopRampA(); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "./AmplificationUtilsV1.sol"; import "./LPToken.sol"; import "./MathUtils.sol"; /** * @title SwapUtils library * @notice A library to be used within Swap.sol. Contains functions responsible for custody and AMM functionalities. * @dev Contracts relying on this library must initialize SwapUtils.Swap struct then use this library * for SwapUtils.Swap struct. Note that this library contains both functions called by users and admins. * Admin functions should be protected within contracts using this library. */ library SwapUtilsV1 { using SafeERC20 for IERC20; using SafeMath for uint256; using MathUtils for uint256; /*** EVENTS ***/ event TokenSwap( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); event AddLiquidity( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event RemoveLiquidity( address indexed provider, uint256[] tokenAmounts, uint256 lpTokenSupply ); event RemoveLiquidityOne( address indexed provider, uint256 lpTokenAmount, uint256 lpTokenSupply, uint256 boughtId, uint256 tokensBought ); event RemoveLiquidityImbalance( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event NewAdminFee(uint256 newAdminFee); event NewSwapFee(uint256 newSwapFee); event NewWithdrawFee(uint256 newWithdrawFee); struct Swap { // variables around the ramp management of A, // the amplification coefficient * n * (n - 1) // see https://www.curve.fi/stableswap-paper.pdf for details uint256 initialA; uint256 futureA; uint256 initialATime; uint256 futureATime; // fee calculation uint256 swapFee; uint256 adminFee; uint256 defaultWithdrawFee; LPToken lpToken; // contract references for all tokens being pooled IERC20[] pooledTokens; // multipliers for each pooled token's precision to get to POOL_PRECISION_DECIMALS // for example, TBTC has 18 decimals, so the multiplier should be 1. WBTC // has 8, so the multiplier should be 10 ** 18 / 10 ** 8 => 10 ** 10 uint256[] tokenPrecisionMultipliers; // the pool balance of each token, in the token's precision // the contract's actual token balance might differ uint256[] balances; mapping(address => uint256) depositTimestamp; mapping(address => uint256) withdrawFeeMultiplier; } // Struct storing variables used in calculations in the // calculateWithdrawOneTokenDY function to avoid stack too deep errors struct CalculateWithdrawOneTokenDYInfo { uint256 d0; uint256 d1; uint256 newY; uint256 feePerToken; uint256 preciseA; } // Struct storing variables used in calculations in the // {add,remove}Liquidity functions to avoid stack too deep errors struct ManageLiquidityInfo { uint256 d0; uint256 d1; uint256 d2; uint256 preciseA; LPToken lpToken; uint256 totalSupply; uint256[] balances; uint256[] multipliers; } // the precision all pools tokens will be converted to uint8 public constant POOL_PRECISION_DECIMALS = 18; // the denominator used to calculate admin and LP fees. For example, an // LP fee might be something like tradeAmount.mul(fee).div(FEE_DENOMINATOR) uint256 private constant FEE_DENOMINATOR = 10**10; // Max swap fee is 1% or 100bps of each swap uint256 public constant MAX_SWAP_FEE = 10**8; // Max adminFee is 100% of the swapFee // adminFee does not add additional fee on top of swapFee // Instead it takes a certain % of the swapFee. Therefore it has no impact on the // users but only on the earnings of LPs uint256 public constant MAX_ADMIN_FEE = 10**10; // Max withdrawFee is 1% of the value withdrawn // Fee will be redistributed to the LPs in the pool, rewarding // long term providers. uint256 public constant MAX_WITHDRAW_FEE = 10**8; // Constant value used as max loop limit uint256 private constant MAX_LOOP_LIMIT = 256; // Time that it should take for the withdraw fee to fully decay to 0 uint256 public constant WITHDRAW_FEE_DECAY_TIME = 4 weeks; /*** VIEW & PURE FUNCTIONS ***/ /** * @notice Retrieves the timestamp of last deposit made by the given address * @param self Swap struct to read from * @return timestamp of last deposit */ function getDepositTimestamp(Swap storage self, address user) external view returns (uint256) { return self.depositTimestamp[user]; } function _getAPrecise(Swap storage self) internal view returns (uint256) { return AmplificationUtilsV1._getAPrecise(self); } /** * @notice Calculate the dy, the amount of selected token that user receives and * the fee of withdrawing in one token * @param account the address that is withdrawing * @param tokenAmount the amount to withdraw in the pool's precision * @param tokenIndex which token will be withdrawn * @param self Swap struct to read from * @return the amount of token user will receive */ function calculateWithdrawOneToken( Swap storage self, address account, uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256) { (uint256 availableTokenAmount, ) = _calculateWithdrawOneToken( self, account, tokenAmount, tokenIndex, self.lpToken.totalSupply() ); return availableTokenAmount; } function _calculateWithdrawOneToken( Swap storage self, address account, uint256 tokenAmount, uint8 tokenIndex, uint256 totalSupply ) internal view returns (uint256, uint256) { uint256 dy; uint256 newY; uint256 currentY; (dy, newY, currentY) = calculateWithdrawOneTokenDY( self, tokenIndex, tokenAmount, totalSupply ); // dy_0 (without fees) // dy, dy_0 - dy uint256 dySwapFee = currentY .sub(newY) .div(self.tokenPrecisionMultipliers[tokenIndex]) .sub(dy); dy = dy .mul( FEE_DENOMINATOR.sub(_calculateCurrentWithdrawFee(self, account)) ) .div(FEE_DENOMINATOR); return (dy, dySwapFee); } /** * @notice Calculate the dy of withdrawing in one token * @param self Swap struct to read from * @param tokenIndex which token will be withdrawn * @param tokenAmount the amount to withdraw in the pools precision * @return the d and the new y after withdrawing one token */ function calculateWithdrawOneTokenDY( Swap storage self, uint8 tokenIndex, uint256 tokenAmount, uint256 totalSupply ) internal view returns ( uint256, uint256, uint256 ) { // Get the current D, then solve the stableswap invariant // y_i for D - tokenAmount uint256[] memory xp = _xp(self); require(tokenIndex < xp.length, "Token index out of range"); CalculateWithdrawOneTokenDYInfo memory v = CalculateWithdrawOneTokenDYInfo(0, 0, 0, 0, 0); v.preciseA = _getAPrecise(self); v.d0 = getD(xp, v.preciseA); v.d1 = v.d0.sub(tokenAmount.mul(v.d0).div(totalSupply)); require(tokenAmount <= xp[tokenIndex], "Withdraw exceeds available"); v.newY = getYD(v.preciseA, tokenIndex, xp, v.d1); uint256[] memory xpReduced = new uint256[](xp.length); v.feePerToken = _feePerToken(self.swapFee, xp.length); for (uint256 i = 0; i < xp.length; i++) { uint256 xpi = xp[i]; // if i == tokenIndex, dxExpected = xp[i] * d1 / d0 - newY // else dxExpected = xp[i] - (xp[i] * d1 / d0) // xpReduced[i] -= dxExpected * fee / FEE_DENOMINATOR xpReduced[i] = xpi.sub( ( (i == tokenIndex) ? xpi.mul(v.d1).div(v.d0).sub(v.newY) : xpi.sub(xpi.mul(v.d1).div(v.d0)) ).mul(v.feePerToken).div(FEE_DENOMINATOR) ); } uint256 dy = xpReduced[tokenIndex].sub( getYD(v.preciseA, tokenIndex, xpReduced, v.d1) ); dy = dy.sub(1).div(self.tokenPrecisionMultipliers[tokenIndex]); return (dy, v.newY, xp[tokenIndex]); } /** * @notice Calculate the price of a token in the pool with given * precision-adjusted balances and a particular D. * * @dev This is accomplished via solving the invariant iteratively. * See the StableSwap paper and Curve.fi implementation for further details. * * x_1**2 + x1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A) * x_1**2 + b*x_1 = c * x_1 = (x_1**2 + c) / (2*x_1 + b) * * @param a the amplification coefficient * n * (n - 1). See the StableSwap paper for details. * @param tokenIndex Index of token we are calculating for. * @param xp a precision-adjusted set of pool balances. Array should be * the same cardinality as the pool. * @param d the stableswap invariant * @return the price of the token, in the same precision as in xp */ function getYD( uint256 a, uint8 tokenIndex, uint256[] memory xp, uint256 d ) internal pure returns (uint256) { uint256 numTokens = xp.length; require(tokenIndex < numTokens, "Token not found"); uint256 c = d; uint256 s; uint256 nA = a.mul(numTokens); for (uint256 i = 0; i < numTokens; i++) { if (i != tokenIndex) { s = s.add(xp[i]); c = c.mul(d).div(xp[i].mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // c = c * D * D * D * ... overflow! } } c = c.mul(d).mul(AmplificationUtilsV1.A_PRECISION).div( nA.mul(numTokens) ); uint256 b = s.add(d.mul(AmplificationUtilsV1.A_PRECISION).div(nA)); uint256 yPrev; uint256 y = d; for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { yPrev = y; y = y.mul(y).add(c).div(y.mul(2).add(b).sub(d)); if (y.within1(yPrev)) { return y; } } revert("Approximation did not converge"); } /** * @notice Get D, the StableSwap invariant, based on a set of balances and a particular A. * @param xp a precision-adjusted set of pool balances. Array should be the same cardinality * as the pool. * @param a the amplification coefficient * n * (n - 1) in A_PRECISION. * See the StableSwap paper for details * @return the invariant, at the precision of the pool */ function getD(uint256[] memory xp, uint256 a) internal pure returns (uint256) { uint256 numTokens = xp.length; uint256 s; for (uint256 i = 0; i < numTokens; i++) { s = s.add(xp[i]); } if (s == 0) { return 0; } uint256 prevD; uint256 d = s; uint256 nA = a.mul(numTokens); for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { uint256 dP = d; for (uint256 j = 0; j < numTokens; j++) { dP = dP.mul(d).div(xp[j].mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // dP = dP * D * D * D * ... overflow! } prevD = d; d = nA .mul(s) .div(AmplificationUtilsV1.A_PRECISION) .add(dP.mul(numTokens)) .mul(d) .div( nA .sub(AmplificationUtilsV1.A_PRECISION) .mul(d) .div(AmplificationUtilsV1.A_PRECISION) .add(numTokens.add(1).mul(dP)) ); if (d.within1(prevD)) { return d; } } // Convergence should occur in 4 loops or less. If this is reached, there may be something wrong // with the pool. If this were to occur repeatedly, LPs should withdraw via `removeLiquidity()` // function which does not rely on D. revert("D does not converge"); } /** * @notice Given a set of balances and precision multipliers, return the * precision-adjusted balances. * * @param balances an array of token balances, in their native precisions. * These should generally correspond with pooled tokens. * * @param precisionMultipliers an array of multipliers, corresponding to * the amounts in the balances array. When multiplied together they * should yield amounts at the pool's precision. * * @return an array of amounts "scaled" to the pool's precision */ function _xp( uint256[] memory balances, uint256[] memory precisionMultipliers ) internal pure returns (uint256[] memory) { uint256 numTokens = balances.length; require( numTokens == precisionMultipliers.length, "Balances must match multipliers" ); uint256[] memory xp = new uint256[](numTokens); for (uint256 i = 0; i < numTokens; i++) { xp[i] = balances[i].mul(precisionMultipliers[i]); } return xp; } /** * @notice Return the precision-adjusted balances of all tokens in the pool * @param self Swap struct to read from * @return the pool balances "scaled" to the pool's precision, allowing * them to be more easily compared. */ function _xp(Swap storage self) internal view returns (uint256[] memory) { return _xp(self.balances, self.tokenPrecisionMultipliers); } /** * @notice Get the virtual price, to help calculate profit * @param self Swap struct to read from * @return the virtual price, scaled to precision of POOL_PRECISION_DECIMALS */ function getVirtualPrice(Swap storage self) external view returns (uint256) { uint256 d = getD(_xp(self), _getAPrecise(self)); LPToken lpToken = self.lpToken; uint256 supply = lpToken.totalSupply(); if (supply > 0) { return d.mul(10**uint256(POOL_PRECISION_DECIMALS)).div(supply); } return 0; } /** * @notice Calculate the new balances of the tokens given the indexes of the token * that is swapped from (FROM) and the token that is swapped to (TO). * This function is used as a helper function to calculate how much TO token * the user should receive on swap. * * @param preciseA precise form of amplification coefficient * @param tokenIndexFrom index of FROM token * @param tokenIndexTo index of TO token * @param x the new total amount of FROM token * @param xp balances of the tokens in the pool * @return the amount of TO token that should remain in the pool */ function getY( uint256 preciseA, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 x, uint256[] memory xp ) internal pure returns (uint256) { uint256 numTokens = xp.length; require( tokenIndexFrom != tokenIndexTo, "Can't compare token to itself" ); require( tokenIndexFrom < numTokens && tokenIndexTo < numTokens, "Tokens must be in pool" ); uint256 d = getD(xp, preciseA); uint256 c = d; uint256 s; uint256 nA = numTokens.mul(preciseA); uint256 _x; for (uint256 i = 0; i < numTokens; i++) { if (i == tokenIndexFrom) { _x = x; } else if (i != tokenIndexTo) { _x = xp[i]; } else { continue; } s = s.add(_x); c = c.mul(d).div(_x.mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // c = c * D * D * D * ... overflow! } c = c.mul(d).mul(AmplificationUtilsV1.A_PRECISION).div( nA.mul(numTokens) ); uint256 b = s.add(d.mul(AmplificationUtilsV1.A_PRECISION).div(nA)); uint256 yPrev; uint256 y = d; // iterative approximation for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { yPrev = y; y = y.mul(y).add(c).div(y.mul(2).add(b).sub(d)); if (y.within1(yPrev)) { return y; } } revert("Approximation did not converge"); } /** * @notice Externally calculates a swap between two tokens. * @param self Swap struct to read from * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @return dy the number of tokens the user will get */ function calculateSwap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256 dy) { (dy, ) = _calculateSwap( self, tokenIndexFrom, tokenIndexTo, dx, self.balances ); } /** * @notice Internally calculates a swap between two tokens. * * @dev The caller is expected to transfer the actual amounts (dx and dy) * using the token contracts. * * @param self Swap struct to read from * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @return dy the number of tokens the user will get * @return dyFee the associated fee */ function _calculateSwap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256[] memory balances ) internal view returns (uint256 dy, uint256 dyFee) { uint256[] memory multipliers = self.tokenPrecisionMultipliers; uint256[] memory xp = _xp(balances, multipliers); require( tokenIndexFrom < xp.length && tokenIndexTo < xp.length, "Token index out of range" ); uint256 x = dx.mul(multipliers[tokenIndexFrom]).add(xp[tokenIndexFrom]); uint256 y = getY( _getAPrecise(self), tokenIndexFrom, tokenIndexTo, x, xp ); dy = xp[tokenIndexTo].sub(y).sub(1); dyFee = dy.mul(self.swapFee).div(FEE_DENOMINATOR); dy = dy.sub(dyFee).div(multipliers[tokenIndexTo]); } /** * @notice A simple method to calculate amount of each underlying * tokens that is returned upon burning given amount of * LP tokens * * @param account the address that is removing liquidity. required for withdraw fee calculation * @param amount the amount of LP tokens that would to be burned on * withdrawal * @return array of amounts of tokens user will receive */ function calculateRemoveLiquidity( Swap storage self, address account, uint256 amount ) external view returns (uint256[] memory) { return _calculateRemoveLiquidity( self, self.balances, account, amount, self.lpToken.totalSupply() ); } function _calculateRemoveLiquidity( Swap storage self, uint256[] memory balances, address account, uint256 amount, uint256 totalSupply ) internal view returns (uint256[] memory) { require(amount <= totalSupply, "Cannot exceed total supply"); uint256 feeAdjustedAmount = amount .mul( FEE_DENOMINATOR.sub(_calculateCurrentWithdrawFee(self, account)) ) .div(FEE_DENOMINATOR); uint256[] memory amounts = new uint256[](balances.length); for (uint256 i = 0; i < balances.length; i++) { amounts[i] = balances[i].mul(feeAdjustedAmount).div(totalSupply); } return amounts; } /** * @notice Calculate the fee that is applied when the given user withdraws. * Withdraw fee decays linearly over WITHDRAW_FEE_DECAY_TIME. * @param user address you want to calculate withdraw fee of * @return current withdraw fee of the user */ function calculateCurrentWithdrawFee(Swap storage self, address user) external view returns (uint256) { return _calculateCurrentWithdrawFee(self, user); } function _calculateCurrentWithdrawFee(Swap storage self, address user) internal view returns (uint256) { uint256 endTime = self.depositTimestamp[user].add( WITHDRAW_FEE_DECAY_TIME ); if (endTime > block.timestamp) { uint256 timeLeftover = endTime.sub(block.timestamp); return self .defaultWithdrawFee .mul(self.withdrawFeeMultiplier[user]) .mul(timeLeftover) .div(WITHDRAW_FEE_DECAY_TIME) .div(FEE_DENOMINATOR); } return 0; } /** * @notice A simple method to calculate prices from deposits or * withdrawals, excluding fees but including slippage. This is * helpful as an input into the various "min" parameters on calls * to fight front-running * * @dev This shouldn't be used outside frontends for user estimates. * * @param self Swap struct to read from * @param account address of the account depositing or withdrawing tokens * @param amounts an array of token amounts to deposit or withdrawal, * corresponding to pooledTokens. The amount should be in each * pooled token's native precision. If a token charges a fee on transfers, * use the amount that gets transferred after the fee. * @param deposit whether this is a deposit or a withdrawal * @return if deposit was true, total amount of lp token that will be minted and if * deposit was false, total amount of lp token that will be burned */ function calculateTokenAmount( Swap storage self, address account, uint256[] calldata amounts, bool deposit ) external view returns (uint256) { uint256 a = _getAPrecise(self); uint256[] memory balances = self.balances; uint256[] memory multipliers = self.tokenPrecisionMultipliers; uint256 d0 = getD(_xp(balances, multipliers), a); for (uint256 i = 0; i < balances.length; i++) { if (deposit) { balances[i] = balances[i].add(amounts[i]); } else { balances[i] = balances[i].sub( amounts[i], "Cannot withdraw more than available" ); } } uint256 d1 = getD(_xp(balances, multipliers), a); uint256 totalSupply = self.lpToken.totalSupply(); if (deposit) { return d1.sub(d0).mul(totalSupply).div(d0); } else { return d0.sub(d1).mul(totalSupply).div(d0).mul(FEE_DENOMINATOR).div( FEE_DENOMINATOR.sub( _calculateCurrentWithdrawFee(self, account) ) ); } } /** * @notice return accumulated amount of admin fees of the token with given index * @param self Swap struct to read from * @param index Index of the pooled token * @return admin balance in the token's precision */ function getAdminBalance(Swap storage self, uint256 index) external view returns (uint256) { require(index < self.pooledTokens.length, "Token index out of range"); return self.pooledTokens[index].balanceOf(address(this)).sub( self.balances[index] ); } /** * @notice internal helper function to calculate fee per token multiplier used in * swap fee calculations * @param swapFee swap fee for the tokens * @param numTokens number of tokens pooled */ function _feePerToken(uint256 swapFee, uint256 numTokens) internal pure returns (uint256) { return swapFee.mul(numTokens).div(numTokens.sub(1).mul(4)); } /*** STATE MODIFYING FUNCTIONS ***/ /** * @notice swap two tokens in the pool * @param self Swap struct to read from and write to * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell * @param minDy the min amount the user would like to receive, or revert. * @return amount of token user received on swap */ function swap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy ) external returns (uint256) { { IERC20 tokenFrom = self.pooledTokens[tokenIndexFrom]; require( dx <= tokenFrom.balanceOf(msg.sender), "Cannot swap more than you own" ); // Transfer tokens first to see if a fee was charged on transfer uint256 beforeBalance = tokenFrom.balanceOf(address(this)); tokenFrom.safeTransferFrom(msg.sender, address(this), dx); // Use the actual transferred amount for AMM math dx = tokenFrom.balanceOf(address(this)).sub(beforeBalance); } uint256 dy; uint256 dyFee; uint256[] memory balances = self.balances; (dy, dyFee) = _calculateSwap( self, tokenIndexFrom, tokenIndexTo, dx, balances ); require(dy >= minDy, "Swap didn't result in min tokens"); uint256 dyAdminFee = dyFee.mul(self.adminFee).div(FEE_DENOMINATOR).div( self.tokenPrecisionMultipliers[tokenIndexTo] ); self.balances[tokenIndexFrom] = balances[tokenIndexFrom].add(dx); self.balances[tokenIndexTo] = balances[tokenIndexTo].sub(dy).sub( dyAdminFee ); self.pooledTokens[tokenIndexTo].safeTransfer(msg.sender, dy); emit TokenSwap(msg.sender, dx, dy, tokenIndexFrom, tokenIndexTo); return dy; } /** * @notice Add liquidity to the pool * @param self Swap struct to read from and write to * @param amounts the amounts of each token to add, in their native precision * @param minToMint the minimum LP tokens adding this amount of liquidity * should mint, otherwise revert. Handy for front-running mitigation * allowed addresses. If the pool is not in the guarded launch phase, this parameter will be ignored. * @return amount of LP token user received */ function addLiquidity( Swap storage self, uint256[] memory amounts, uint256 minToMint ) external returns (uint256) { IERC20[] memory pooledTokens = self.pooledTokens; require( amounts.length == pooledTokens.length, "Amounts must match pooled tokens" ); // current state ManageLiquidityInfo memory v = ManageLiquidityInfo( 0, 0, 0, _getAPrecise(self), self.lpToken, 0, self.balances, self.tokenPrecisionMultipliers ); v.totalSupply = v.lpToken.totalSupply(); if (v.totalSupply != 0) { v.d0 = getD(_xp(v.balances, v.multipliers), v.preciseA); } uint256[] memory newBalances = new uint256[](pooledTokens.length); for (uint256 i = 0; i < pooledTokens.length; i++) { require( v.totalSupply != 0 || amounts[i] > 0, "Must supply all tokens in pool" ); // Transfer tokens first to see if a fee was charged on transfer if (amounts[i] != 0) { uint256 beforeBalance = pooledTokens[i].balanceOf( address(this) ); pooledTokens[i].safeTransferFrom( msg.sender, address(this), amounts[i] ); // Update the amounts[] with actual transfer amount amounts[i] = pooledTokens[i].balanceOf(address(this)).sub( beforeBalance ); } newBalances[i] = v.balances[i].add(amounts[i]); } // invariant after change v.d1 = getD(_xp(newBalances, v.multipliers), v.preciseA); require(v.d1 > v.d0, "D should increase"); // updated to reflect fees and calculate the user's LP tokens v.d2 = v.d1; uint256[] memory fees = new uint256[](pooledTokens.length); if (v.totalSupply != 0) { uint256 feePerToken = _feePerToken( self.swapFee, pooledTokens.length ); for (uint256 i = 0; i < pooledTokens.length; i++) { uint256 idealBalance = v.d1.mul(v.balances[i]).div(v.d0); fees[i] = feePerToken .mul(idealBalance.difference(newBalances[i])) .div(FEE_DENOMINATOR); self.balances[i] = newBalances[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); newBalances[i] = newBalances[i].sub(fees[i]); } v.d2 = getD(_xp(newBalances, v.multipliers), v.preciseA); } else { // the initial depositor doesn't pay fees self.balances = newBalances; } uint256 toMint; if (v.totalSupply == 0) { toMint = v.d1; } else { toMint = v.d2.sub(v.d0).mul(v.totalSupply).div(v.d0); } require(toMint >= minToMint, "Couldn't mint min requested"); // mint the user's LP tokens v.lpToken.mint(msg.sender, toMint); emit AddLiquidity( msg.sender, amounts, fees, v.d1, v.totalSupply.add(toMint) ); return toMint; } /** * @notice Update the withdraw fee for `user`. If the user is currently * not providing liquidity in the pool, sets to default value. If not, recalculate * the starting withdraw fee based on the last deposit's time & amount relative * to the new deposit. * * @param self Swap struct to read from and write to * @param user address of the user depositing tokens * @param toMint amount of pool tokens to be minted */ function updateUserWithdrawFee( Swap storage self, address user, uint256 toMint ) public { // If token is transferred to address 0 (or burned), don't update the fee. if (user == address(0)) { return; } if (self.defaultWithdrawFee == 0) { // If current fee is set to 0%, set multiplier to FEE_DENOMINATOR self.withdrawFeeMultiplier[user] = FEE_DENOMINATOR; } else { // Otherwise, calculate appropriate discount based on last deposit amount uint256 currentFee = _calculateCurrentWithdrawFee(self, user); uint256 currentBalance = self.lpToken.balanceOf(user); // ((currentBalance * currentFee) + (toMint * defaultWithdrawFee)) * FEE_DENOMINATOR / // ((toMint + currentBalance) * defaultWithdrawFee) self.withdrawFeeMultiplier[user] = currentBalance .mul(currentFee) .add(toMint.mul(self.defaultWithdrawFee)) .mul(FEE_DENOMINATOR) .div(toMint.add(currentBalance).mul(self.defaultWithdrawFee)); } self.depositTimestamp[user] = block.timestamp; } /** * @notice Burn LP tokens to remove liquidity from the pool. * @dev Liquidity can always be removed, even when the pool is paused. * @param self Swap struct to read from and write to * @param amount the amount of LP tokens to burn * @param minAmounts the minimum amounts of each token in the pool * acceptable for this burn. Useful as a front-running mitigation * @return amounts of tokens the user received */ function removeLiquidity( Swap storage self, uint256 amount, uint256[] calldata minAmounts ) external returns (uint256[] memory) { LPToken lpToken = self.lpToken; IERC20[] memory pooledTokens = self.pooledTokens; require(amount <= lpToken.balanceOf(msg.sender), ">LP.balanceOf"); require( minAmounts.length == pooledTokens.length, "minAmounts must match poolTokens" ); uint256[] memory balances = self.balances; uint256 totalSupply = lpToken.totalSupply(); uint256[] memory amounts = _calculateRemoveLiquidity( self, balances, msg.sender, amount, totalSupply ); for (uint256 i = 0; i < amounts.length; i++) { require(amounts[i] >= minAmounts[i], "amounts[i] < minAmounts[i]"); self.balances[i] = balances[i].sub(amounts[i]); pooledTokens[i].safeTransfer(msg.sender, amounts[i]); } lpToken.burnFrom(msg.sender, amount); emit RemoveLiquidity(msg.sender, amounts, totalSupply.sub(amount)); return amounts; } /** * @notice Remove liquidity from the pool all in one token. * @param self Swap struct to read from and write to * @param tokenAmount the amount of the lp tokens to burn * @param tokenIndex the index of the token you want to receive * @param minAmount the minimum amount to withdraw, otherwise revert * @return amount chosen token that user received */ function removeLiquidityOneToken( Swap storage self, uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount ) external returns (uint256) { LPToken lpToken = self.lpToken; IERC20[] memory pooledTokens = self.pooledTokens; require(tokenAmount <= lpToken.balanceOf(msg.sender), ">LP.balanceOf"); require(tokenIndex < pooledTokens.length, "Token not found"); uint256 totalSupply = lpToken.totalSupply(); (uint256 dy, uint256 dyFee) = _calculateWithdrawOneToken( self, msg.sender, tokenAmount, tokenIndex, totalSupply ); require(dy >= minAmount, "dy < minAmount"); self.balances[tokenIndex] = self.balances[tokenIndex].sub( dy.add(dyFee.mul(self.adminFee).div(FEE_DENOMINATOR)) ); lpToken.burnFrom(msg.sender, tokenAmount); pooledTokens[tokenIndex].safeTransfer(msg.sender, dy); emit RemoveLiquidityOne( msg.sender, tokenAmount, totalSupply, tokenIndex, dy ); return dy; } /** * @notice Remove liquidity from the pool, weighted differently than the * pool's current balances. * * @param self Swap struct to read from and write to * @param amounts how much of each token to withdraw * @param maxBurnAmount the max LP token provider is willing to pay to * remove liquidity. Useful as a front-running mitigation. * @return actual amount of LP tokens burned in the withdrawal */ function removeLiquidityImbalance( Swap storage self, uint256[] memory amounts, uint256 maxBurnAmount ) public returns (uint256) { ManageLiquidityInfo memory v = ManageLiquidityInfo( 0, 0, 0, _getAPrecise(self), self.lpToken, 0, self.balances, self.tokenPrecisionMultipliers ); v.totalSupply = v.lpToken.totalSupply(); IERC20[] memory pooledTokens = self.pooledTokens; require( amounts.length == pooledTokens.length, "Amounts should match pool tokens" ); require( maxBurnAmount <= v.lpToken.balanceOf(msg.sender) && maxBurnAmount != 0, ">LP.balanceOf" ); uint256 feePerToken = _feePerToken(self.swapFee, pooledTokens.length); uint256[] memory fees = new uint256[](pooledTokens.length); { uint256[] memory balances1 = new uint256[](pooledTokens.length); v.d0 = getD(_xp(v.balances, v.multipliers), v.preciseA); for (uint256 i = 0; i < pooledTokens.length; i++) { balances1[i] = v.balances[i].sub( amounts[i], "Cannot withdraw more than available" ); } v.d1 = getD(_xp(balances1, v.multipliers), v.preciseA); for (uint256 i = 0; i < pooledTokens.length; i++) { uint256 idealBalance = v.d1.mul(v.balances[i]).div(v.d0); uint256 difference = idealBalance.difference(balances1[i]); fees[i] = feePerToken.mul(difference).div(FEE_DENOMINATOR); self.balances[i] = balances1[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); balances1[i] = balances1[i].sub(fees[i]); } v.d2 = getD(_xp(balances1, v.multipliers), v.preciseA); } uint256 tokenAmount = v.d0.sub(v.d2).mul(v.totalSupply).div(v.d0); require(tokenAmount != 0, "Burnt amount cannot be zero"); tokenAmount = tokenAmount.add(1).mul(FEE_DENOMINATOR).div( FEE_DENOMINATOR.sub(_calculateCurrentWithdrawFee(self, msg.sender)) ); require(tokenAmount <= maxBurnAmount, "tokenAmount > maxBurnAmount"); v.lpToken.burnFrom(msg.sender, tokenAmount); for (uint256 i = 0; i < pooledTokens.length; i++) { pooledTokens[i].safeTransfer(msg.sender, amounts[i]); } emit RemoveLiquidityImbalance( msg.sender, amounts, fees, v.d1, v.totalSupply.sub(tokenAmount) ); return tokenAmount; } /** * @notice withdraw all admin fees to a given address * @param self Swap struct to withdraw fees from * @param to Address to send the fees to */ function withdrawAdminFees(Swap storage self, address to) external { IERC20[] memory pooledTokens = self.pooledTokens; for (uint256 i = 0; i < pooledTokens.length; i++) { IERC20 token = pooledTokens[i]; uint256 balance = token.balanceOf(address(this)).sub( self.balances[i] ); if (balance != 0) { token.safeTransfer(to, balance); } } } /** * @notice Sets the admin fee * @dev adminFee cannot be higher than 100% of the swap fee * @param self Swap struct to update * @param newAdminFee new admin fee to be applied on future transactions */ function setAdminFee(Swap storage self, uint256 newAdminFee) external { require(newAdminFee <= MAX_ADMIN_FEE, "Fee is too high"); self.adminFee = newAdminFee; emit NewAdminFee(newAdminFee); } /** * @notice update the swap fee * @dev fee cannot be higher than 1% of each swap * @param self Swap struct to update * @param newSwapFee new swap fee to be applied on future transactions */ function setSwapFee(Swap storage self, uint256 newSwapFee) external { require(newSwapFee <= MAX_SWAP_FEE, "Fee is too high"); self.swapFee = newSwapFee; emit NewSwapFee(newSwapFee); } /** * @notice update the default withdraw fee. This also affects deposits made in the past as well. * @param self Swap struct to update * @param newWithdrawFee new withdraw fee to be applied */ function setDefaultWithdrawFee(Swap storage self, uint256 newWithdrawFee) external { require(newWithdrawFee <= MAX_WITHDRAW_FEE, "Fee is too high"); self.defaultWithdrawFee = newWithdrawFee; emit NewWithdrawFee(newWithdrawFee); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "./SwapUtilsV1.sol"; /** * @title AmplificationUtils library * @notice A library to calculate and ramp the A parameter of a given `SwapUtils.Swap` struct. * This library assumes the struct is fully validated. */ library AmplificationUtilsV1 { using SafeMath for uint256; event RampA( uint256 oldA, uint256 newA, uint256 initialTime, uint256 futureTime ); event StopRampA(uint256 currentA, uint256 time); // Constant values used in ramping A calculations uint256 public constant A_PRECISION = 100; uint256 public constant MAX_A = 10**6; uint256 private constant MAX_A_CHANGE = 2; uint256 private constant MIN_RAMP_TIME = 14 days; /** * @notice Return A, the amplification coefficient * n * (n - 1) * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter */ function getA(SwapUtilsV1.Swap storage self) external view returns (uint256) { return _getAPrecise(self).div(A_PRECISION); } /** * @notice Return A in its raw precision * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter in its raw precision form */ function getAPrecise(SwapUtilsV1.Swap storage self) external view returns (uint256) { return _getAPrecise(self); } /** * @notice Return A in its raw precision * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter in its raw precision form */ function _getAPrecise(SwapUtilsV1.Swap storage self) internal view returns (uint256) { uint256 t1 = self.futureATime; // time when ramp is finished uint256 a1 = self.futureA; // final A value when ramp is finished if (block.timestamp < t1) { uint256 t0 = self.initialATime; // time when ramp is started uint256 a0 = self.initialA; // initial A value when ramp is started if (a1 > a0) { // a0 + (a1 - a0) * (block.timestamp - t0) / (t1 - t0) return a0.add( a1.sub(a0).mul(block.timestamp.sub(t0)).div(t1.sub(t0)) ); } else { // a0 - (a0 - a1) * (block.timestamp - t0) / (t1 - t0) return a0.sub( a0.sub(a1).mul(block.timestamp.sub(t0)).div(t1.sub(t0)) ); } } else { return a1; } } /** * @notice Start ramping up or down A parameter towards given futureA_ and futureTime_ * Checks if the change is too rapid, and commits the new A value only when it falls under * the limit range. * @param self Swap struct to update * @param futureA_ the new A to ramp towards * @param futureTime_ timestamp when the new A should be reached */ function rampA( SwapUtilsV1.Swap storage self, uint256 futureA_, uint256 futureTime_ ) external { require( block.timestamp >= self.initialATime.add(1 days), "Wait 1 day before starting ramp" ); require( futureTime_ >= block.timestamp.add(MIN_RAMP_TIME), "Insufficient ramp time" ); require( futureA_ > 0 && futureA_ < MAX_A, "futureA_ must be > 0 and < MAX_A" ); uint256 initialAPrecise = _getAPrecise(self); uint256 futureAPrecise = futureA_.mul(A_PRECISION); if (futureAPrecise < initialAPrecise) { require( futureAPrecise.mul(MAX_A_CHANGE) >= initialAPrecise, "futureA_ is too small" ); } else { require( futureAPrecise <= initialAPrecise.mul(MAX_A_CHANGE), "futureA_ is too large" ); } self.initialA = initialAPrecise; self.futureA = futureAPrecise; self.initialATime = block.timestamp; self.futureATime = futureTime_; emit RampA( initialAPrecise, futureAPrecise, block.timestamp, futureTime_ ); } /** * @notice Stops ramping A immediately. Once this function is called, rampA() * cannot be called for another 24 hours * @param self Swap struct to update */ function stopRampA(SwapUtilsV1.Swap storage self) external { require(self.futureATime > block.timestamp, "Ramp is already stopped"); uint256 currentA = _getAPrecise(self); self.initialA = currentA; self.futureA = currentA; self.initialATime = block.timestamp; self.futureATime = block.timestamp; emit StopRampA(currentA, block.timestamp); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../LPToken.sol"; import "../interfaces/ISwap.sol"; import "../MathUtils.sol"; import "../SwapUtils.sol"; /** * @title MetaSwapUtils library * @notice A library to be used within MetaSwap.sol. Contains functions responsible for custody and AMM functionalities. * * MetaSwap is a modified version of Swap that allows Swap's LP token to be utilized in pooling with other tokens. * As an example, if there is a Swap pool consisting of [DAI, USDC, USDT]. Then a MetaSwap pool can be created * with [sUSD, BaseSwapLPToken] to allow trades between either the LP token or the underlying tokens and sUSD. * * @dev Contracts relying on this library must initialize SwapUtils.Swap struct then use this library * for SwapUtils.Swap struct. Note that this library contains both functions called by users and admins. * Admin functions should be protected within contracts using this library. */ library MetaSwapUtils { using SafeERC20 for IERC20; using SafeMath for uint256; using MathUtils for uint256; using AmplificationUtils for SwapUtils.Swap; /*** EVENTS ***/ event TokenSwap( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); event TokenSwapUnderlying( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); event AddLiquidity( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event RemoveLiquidityOne( address indexed provider, uint256 lpTokenAmount, uint256 lpTokenSupply, uint256 boughtId, uint256 tokensBought ); event RemoveLiquidityImbalance( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event NewAdminFee(uint256 newAdminFee); event NewSwapFee(uint256 newSwapFee); event NewWithdrawFee(uint256 newWithdrawFee); struct MetaSwap { // Meta-Swap related parameters ISwap baseSwap; uint256 baseVirtualPrice; uint256 baseCacheLastUpdated; IERC20[] baseTokens; } // Struct storing variables used in calculations in the // calculateWithdrawOneTokenDY function to avoid stack too deep errors struct CalculateWithdrawOneTokenDYInfo { uint256 d0; uint256 d1; uint256 newY; uint256 feePerToken; uint256 preciseA; uint256 xpi; } // Struct storing variables used in calculation in removeLiquidityImbalance function // to avoid stack too deep error struct ManageLiquidityInfo { uint256 d0; uint256 d1; uint256 d2; LPToken lpToken; uint256 totalSupply; uint256 preciseA; uint256 baseVirtualPrice; uint256[] tokenPrecisionMultipliers; uint256[] newBalances; } struct SwapUnderlyingInfo { uint256 x; uint256 dx; uint256 dy; uint256[] tokenPrecisionMultipliers; uint256[] oldBalances; IERC20[] baseTokens; IERC20 tokenFrom; uint8 metaIndexFrom; IERC20 tokenTo; uint8 metaIndexTo; uint256 baseVirtualPrice; } struct CalculateSwapUnderlyingInfo { uint256 baseVirtualPrice; ISwap baseSwap; uint8 baseLPTokenIndex; uint8 baseTokensLength; uint8 metaIndexTo; uint256 x; uint256 dy; } // the denominator used to calculate admin and LP fees. For example, an // LP fee might be something like tradeAmount.mul(fee).div(FEE_DENOMINATOR) uint256 private constant FEE_DENOMINATOR = 10**10; // Cache expire time for the stored value of base Swap's virtual price uint256 public constant BASE_CACHE_EXPIRE_TIME = 10 minutes; uint256 public constant BASE_VIRTUAL_PRICE_PRECISION = 10**18; /*** VIEW & PURE FUNCTIONS ***/ /** * @notice Return the stored value of base Swap's virtual price. If * value was updated past BASE_CACHE_EXPIRE_TIME, then read it directly * from the base Swap contract. * @param metaSwapStorage MetaSwap struct to read from * @return base Swap's virtual price */ function _getBaseVirtualPrice(MetaSwap storage metaSwapStorage) internal view returns (uint256) { if ( block.timestamp > metaSwapStorage.baseCacheLastUpdated + BASE_CACHE_EXPIRE_TIME ) { return metaSwapStorage.baseSwap.getVirtualPrice(); } return metaSwapStorage.baseVirtualPrice; } /** * @notice Calculate how much the user would receive when withdrawing via single token * @param self Swap struct to read from * @param metaSwapStorage MetaSwap struct to read from * @param tokenAmount the amount to withdraw in the pool's precision * @param tokenIndex which token will be withdrawn * @return dy the amount of token user will receive */ function calculateWithdrawOneToken( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256 dy) { (dy, ) = _calculateWithdrawOneToken( self, tokenAmount, tokenIndex, _getBaseVirtualPrice(metaSwapStorage), self.lpToken.totalSupply() ); } function _calculateWithdrawOneToken( SwapUtils.Swap storage self, uint256 tokenAmount, uint8 tokenIndex, uint256 baseVirtualPrice, uint256 totalSupply ) internal view returns (uint256, uint256) { uint256 dy; uint256 dySwapFee; { uint256 currentY; uint256 newY; // Calculate how much to withdraw (dy, newY, currentY) = _calculateWithdrawOneTokenDY( self, tokenIndex, tokenAmount, baseVirtualPrice, totalSupply ); // Calculate the associated swap fee dySwapFee = currentY .sub(newY) .div(self.tokenPrecisionMultipliers[tokenIndex]) .sub(dy); } return (dy, dySwapFee); } /** * @notice Calculate the dy of withdrawing in one token * @param self Swap struct to read from * @param tokenIndex which token will be withdrawn * @param tokenAmount the amount to withdraw in the pools precision * @param baseVirtualPrice the virtual price of the base swap's LP token * @return the dy excluding swap fee, the new y after withdrawing one token, and current y */ function _calculateWithdrawOneTokenDY( SwapUtils.Swap storage self, uint8 tokenIndex, uint256 tokenAmount, uint256 baseVirtualPrice, uint256 totalSupply ) internal view returns ( uint256, uint256, uint256 ) { // Get the current D, then solve the stableswap invariant // y_i for D - tokenAmount uint256[] memory xp = _xp(self, baseVirtualPrice); require(tokenIndex < xp.length, "Token index out of range"); CalculateWithdrawOneTokenDYInfo memory v = CalculateWithdrawOneTokenDYInfo( 0, 0, 0, 0, self._getAPrecise(), 0 ); v.d0 = SwapUtils.getD(xp, v.preciseA); v.d1 = v.d0.sub(tokenAmount.mul(v.d0).div(totalSupply)); require(tokenAmount <= xp[tokenIndex], "Withdraw exceeds available"); v.newY = SwapUtils.getYD(v.preciseA, tokenIndex, xp, v.d1); uint256[] memory xpReduced = new uint256[](xp.length); v.feePerToken = SwapUtils._feePerToken(self.swapFee, xp.length); for (uint256 i = 0; i < xp.length; i++) { v.xpi = xp[i]; // if i == tokenIndex, dxExpected = xp[i] * d1 / d0 - newY // else dxExpected = xp[i] - (xp[i] * d1 / d0) // xpReduced[i] -= dxExpected * fee / FEE_DENOMINATOR xpReduced[i] = v.xpi.sub( ( (i == tokenIndex) ? v.xpi.mul(v.d1).div(v.d0).sub(v.newY) : v.xpi.sub(v.xpi.mul(v.d1).div(v.d0)) ).mul(v.feePerToken).div(FEE_DENOMINATOR) ); } uint256 dy = xpReduced[tokenIndex].sub( SwapUtils.getYD(v.preciseA, tokenIndex, xpReduced, v.d1) ); if (tokenIndex == xp.length.sub(1)) { dy = dy.mul(BASE_VIRTUAL_PRICE_PRECISION).div(baseVirtualPrice); } dy = dy.sub(1).div(self.tokenPrecisionMultipliers[tokenIndex]); return (dy, v.newY, xp[tokenIndex]); } /** * @notice Given a set of balances and precision multipliers, return the * precision-adjusted balances. The last element will also get scaled up by * the given baseVirtualPrice. * * @param balances an array of token balances, in their native precisions. * These should generally correspond with pooled tokens. * * @param precisionMultipliers an array of multipliers, corresponding to * the amounts in the balances array. When multiplied together they * should yield amounts at the pool's precision. * * @param baseVirtualPrice the base virtual price to scale the balance of the * base Swap's LP token. * * @return an array of amounts "scaled" to the pool's precision */ function _xp( uint256[] memory balances, uint256[] memory precisionMultipliers, uint256 baseVirtualPrice ) internal pure returns (uint256[] memory) { uint256[] memory xp = SwapUtils._xp(balances, precisionMultipliers); uint256 baseLPTokenIndex = balances.length.sub(1); xp[baseLPTokenIndex] = xp[baseLPTokenIndex].mul(baseVirtualPrice).div( BASE_VIRTUAL_PRICE_PRECISION ); return xp; } /** * @notice Return the precision-adjusted balances of all tokens in the pool * @param self Swap struct to read from * @return the pool balances "scaled" to the pool's precision, allowing * them to be more easily compared. */ function _xp(SwapUtils.Swap storage self, uint256 baseVirtualPrice) internal view returns (uint256[] memory) { return _xp( self.balances, self.tokenPrecisionMultipliers, baseVirtualPrice ); } /** * @notice Get the virtual price, to help calculate profit * @param self Swap struct to read from * @param metaSwapStorage MetaSwap struct to read from * @return the virtual price, scaled to precision of BASE_VIRTUAL_PRICE_PRECISION */ function getVirtualPrice( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage ) external view returns (uint256) { uint256 d = SwapUtils.getD( _xp( self.balances, self.tokenPrecisionMultipliers, _getBaseVirtualPrice(metaSwapStorage) ), self._getAPrecise() ); uint256 supply = self.lpToken.totalSupply(); if (supply != 0) { return d.mul(BASE_VIRTUAL_PRICE_PRECISION).div(supply); } return 0; } /** * @notice Externally calculates a swap between two tokens. The SwapUtils.Swap storage and * MetaSwap storage should be from the same MetaSwap contract. * @param self Swap struct to read from * @param metaSwapStorage MetaSwap struct from the same contract * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @return dy the number of tokens the user will get */ function calculateSwap( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256 dy) { (dy, ) = _calculateSwap( self, tokenIndexFrom, tokenIndexTo, dx, _getBaseVirtualPrice(metaSwapStorage) ); } /** * @notice Internally calculates a swap between two tokens. * * @dev The caller is expected to transfer the actual amounts (dx and dy) * using the token contracts. * * @param self Swap struct to read from * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @param baseVirtualPrice the virtual price of the base LP token * @return dy the number of tokens the user will get and dyFee the associated fee */ function _calculateSwap( SwapUtils.Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 baseVirtualPrice ) internal view returns (uint256 dy, uint256 dyFee) { uint256[] memory xp = _xp(self, baseVirtualPrice); require( tokenIndexFrom < xp.length && tokenIndexTo < xp.length, "Token index out of range" ); uint256 x = dx.mul(self.tokenPrecisionMultipliers[tokenIndexFrom]).add( xp[tokenIndexFrom] ); uint256 y = SwapUtils.getY( self._getAPrecise(), tokenIndexFrom, tokenIndexTo, x, xp ); dy = xp[tokenIndexTo].sub(y).sub(1); dyFee = dy.mul(self.swapFee).div(FEE_DENOMINATOR); dy = dy.sub(dyFee).div(self.tokenPrecisionMultipliers[tokenIndexTo]); } /** * @notice Calculates the expected return amount from swapping between * the pooled tokens and the underlying tokens of the base Swap pool. * * @param self Swap struct to read from * @param metaSwapStorage MetaSwap struct from the same contract * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @return dy the number of tokens the user will get */ function calculateSwapUnderlying( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256) { CalculateSwapUnderlyingInfo memory v = CalculateSwapUnderlyingInfo( _getBaseVirtualPrice(metaSwapStorage), metaSwapStorage.baseSwap, 0, uint8(metaSwapStorage.baseTokens.length), 0, 0, 0 ); uint256[] memory xp = _xp(self, v.baseVirtualPrice); v.baseLPTokenIndex = uint8(xp.length.sub(1)); { uint8 maxRange = v.baseLPTokenIndex + v.baseTokensLength; require( tokenIndexFrom < maxRange && tokenIndexTo < maxRange, "Token index out of range" ); } if (tokenIndexFrom < v.baseLPTokenIndex) { // tokenFrom is from this pool v.x = xp[tokenIndexFrom].add( dx.mul(self.tokenPrecisionMultipliers[tokenIndexFrom]) ); } else { // tokenFrom is from the base pool tokenIndexFrom = tokenIndexFrom - v.baseLPTokenIndex; if (tokenIndexTo < v.baseLPTokenIndex) { uint256[] memory baseInputs = new uint256[](v.baseTokensLength); baseInputs[tokenIndexFrom] = dx; v.x = v .baseSwap .calculateTokenAmount(baseInputs, true) .mul(v.baseVirtualPrice) .div(BASE_VIRTUAL_PRICE_PRECISION) .add(xp[v.baseLPTokenIndex]); } else { // both from and to are from the base pool return v.baseSwap.calculateSwap( tokenIndexFrom, tokenIndexTo - v.baseLPTokenIndex, dx ); } tokenIndexFrom = v.baseLPTokenIndex; } v.metaIndexTo = v.baseLPTokenIndex; if (tokenIndexTo < v.baseLPTokenIndex) { v.metaIndexTo = tokenIndexTo; } { uint256 y = SwapUtils.getY( self._getAPrecise(), tokenIndexFrom, v.metaIndexTo, v.x, xp ); v.dy = xp[v.metaIndexTo].sub(y).sub(1); uint256 dyFee = v.dy.mul(self.swapFee).div(FEE_DENOMINATOR); v.dy = v.dy.sub(dyFee); } if (tokenIndexTo < v.baseLPTokenIndex) { // tokenTo is from this pool v.dy = v.dy.div(self.tokenPrecisionMultipliers[v.metaIndexTo]); } else { // tokenTo is from the base pool v.dy = v.baseSwap.calculateRemoveLiquidityOneToken( v.dy.mul(BASE_VIRTUAL_PRICE_PRECISION).div(v.baseVirtualPrice), tokenIndexTo - v.baseLPTokenIndex ); } return v.dy; } /** * @notice A simple method to calculate prices from deposits or * withdrawals, excluding fees but including slippage. This is * helpful as an input into the various "min" parameters on calls * to fight front-running * * @dev This shouldn't be used outside frontends for user estimates. * * @param self Swap struct to read from * @param metaSwapStorage MetaSwap struct to read from * @param amounts an array of token amounts to deposit or withdrawal, * corresponding to pooledTokens. The amount should be in each * pooled token's native precision. If a token charges a fee on transfers, * use the amount that gets transferred after the fee. * @param deposit whether this is a deposit or a withdrawal * @return if deposit was true, total amount of lp token that will be minted and if * deposit was false, total amount of lp token that will be burned */ function calculateTokenAmount( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint256[] calldata amounts, bool deposit ) external view returns (uint256) { uint256 a = self._getAPrecise(); uint256 d0; uint256 d1; { uint256 baseVirtualPrice = _getBaseVirtualPrice(metaSwapStorage); uint256[] memory balances1 = self.balances; uint256[] memory tokenPrecisionMultipliers = self .tokenPrecisionMultipliers; uint256 numTokens = balances1.length; d0 = SwapUtils.getD( _xp(balances1, tokenPrecisionMultipliers, baseVirtualPrice), a ); for (uint256 i = 0; i < numTokens; i++) { if (deposit) { balances1[i] = balances1[i].add(amounts[i]); } else { balances1[i] = balances1[i].sub( amounts[i], "Cannot withdraw more than available" ); } } d1 = SwapUtils.getD( _xp(balances1, tokenPrecisionMultipliers, baseVirtualPrice), a ); } uint256 totalSupply = self.lpToken.totalSupply(); if (deposit) { return d1.sub(d0).mul(totalSupply).div(d0); } else { return d0.sub(d1).mul(totalSupply).div(d0); } } /*** STATE MODIFYING FUNCTIONS ***/ /** * @notice swap two tokens in the pool * @param self Swap struct to read from and write to * @param metaSwapStorage MetaSwap struct to read from and write to * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell * @param minDy the min amount the user would like to receive, or revert. * @return amount of token user received on swap */ function swap( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy ) external returns (uint256) { { uint256 pooledTokensLength = self.pooledTokens.length; require( tokenIndexFrom < pooledTokensLength && tokenIndexTo < pooledTokensLength, "Token index is out of range" ); } uint256 transferredDx; { IERC20 tokenFrom = self.pooledTokens[tokenIndexFrom]; require( dx <= tokenFrom.balanceOf(msg.sender), "Cannot swap more than you own" ); { // Transfer tokens first to see if a fee was charged on transfer uint256 beforeBalance = tokenFrom.balanceOf(address(this)); tokenFrom.safeTransferFrom(msg.sender, address(this), dx); // Use the actual transferred amount for AMM math transferredDx = tokenFrom.balanceOf(address(this)).sub( beforeBalance ); } } (uint256 dy, uint256 dyFee) = _calculateSwap( self, tokenIndexFrom, tokenIndexTo, transferredDx, _updateBaseVirtualPrice(metaSwapStorage) ); require(dy >= minDy, "Swap didn't result in min tokens"); uint256 dyAdminFee = dyFee.mul(self.adminFee).div(FEE_DENOMINATOR).div( self.tokenPrecisionMultipliers[tokenIndexTo] ); self.balances[tokenIndexFrom] = self.balances[tokenIndexFrom].add( transferredDx ); self.balances[tokenIndexTo] = self.balances[tokenIndexTo].sub(dy).sub( dyAdminFee ); self.pooledTokens[tokenIndexTo].safeTransfer(msg.sender, dy); emit TokenSwap( msg.sender, transferredDx, dy, tokenIndexFrom, tokenIndexTo ); return dy; } /** * @notice Swaps with the underlying tokens of the base Swap pool. For this function, * the token indices are flattened out so that underlying tokens are represented * in the indices. * @dev Since this calls multiple external functions during the execution, * it is recommended to protect any function that depends on this with reentrancy guards. * @param self Swap struct to read from and write to * @param metaSwapStorage MetaSwap struct to read from and write to * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell * @param minDy the min amount the user would like to receive, or revert. * @return amount of token user received on swap */ function swapUnderlying( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy ) external returns (uint256) { SwapUnderlyingInfo memory v = SwapUnderlyingInfo( 0, 0, 0, self.tokenPrecisionMultipliers, self.balances, metaSwapStorage.baseTokens, IERC20(address(0)), 0, IERC20(address(0)), 0, _updateBaseVirtualPrice(metaSwapStorage) ); uint8 baseLPTokenIndex = uint8(v.oldBalances.length.sub(1)); { uint8 maxRange = uint8(baseLPTokenIndex + v.baseTokens.length); require( tokenIndexFrom < maxRange && tokenIndexTo < maxRange, "Token index out of range" ); } ISwap baseSwap = metaSwapStorage.baseSwap; // Find the address of the token swapping from and the index in MetaSwap's token list if (tokenIndexFrom < baseLPTokenIndex) { v.tokenFrom = self.pooledTokens[tokenIndexFrom]; v.metaIndexFrom = tokenIndexFrom; } else { v.tokenFrom = v.baseTokens[tokenIndexFrom - baseLPTokenIndex]; v.metaIndexFrom = baseLPTokenIndex; } // Find the address of the token swapping to and the index in MetaSwap's token list if (tokenIndexTo < baseLPTokenIndex) { v.tokenTo = self.pooledTokens[tokenIndexTo]; v.metaIndexTo = tokenIndexTo; } else { v.tokenTo = v.baseTokens[tokenIndexTo - baseLPTokenIndex]; v.metaIndexTo = baseLPTokenIndex; } // Check for possible fee on transfer v.dx = v.tokenFrom.balanceOf(address(this)); v.tokenFrom.safeTransferFrom(msg.sender, address(this), dx); v.dx = v.tokenFrom.balanceOf(address(this)).sub(v.dx); // update dx in case of fee on transfer if ( tokenIndexFrom < baseLPTokenIndex || tokenIndexTo < baseLPTokenIndex ) { // Either one of the tokens belongs to the MetaSwap tokens list uint256[] memory xp = _xp( v.oldBalances, v.tokenPrecisionMultipliers, v.baseVirtualPrice ); if (tokenIndexFrom < baseLPTokenIndex) { // Swapping from a MetaSwap token v.x = xp[tokenIndexFrom].add( dx.mul(v.tokenPrecisionMultipliers[tokenIndexFrom]) ); } else { // Swapping from a base Swap token // This case requires adding the underlying token to the base Swap, then // using the base LP token to swap to the desired token uint256[] memory baseAmounts = new uint256[]( v.baseTokens.length ); baseAmounts[tokenIndexFrom - baseLPTokenIndex] = v.dx; // Add liquidity to the underlying Swap contract and receive base LP token v.dx = baseSwap.addLiquidity(baseAmounts, 0, block.timestamp); // Calculate the value of total amount of baseLPToken we end up with v.x = v .dx .mul(v.baseVirtualPrice) .div(BASE_VIRTUAL_PRICE_PRECISION) .add(xp[baseLPTokenIndex]); } // Calculate how much to withdraw in MetaSwap level and the the associated swap fee uint256 dyFee; { uint256 y = SwapUtils.getY( self._getAPrecise(), v.metaIndexFrom, v.metaIndexTo, v.x, xp ); v.dy = xp[v.metaIndexTo].sub(y).sub(1); dyFee = v.dy.mul(self.swapFee).div(FEE_DENOMINATOR); v.dy = v.dy.sub(dyFee).div( v.tokenPrecisionMultipliers[v.metaIndexTo] ); } if (tokenIndexTo >= baseLPTokenIndex) { // When swapping to a base Swap token, scale down dy by its virtual price v.dy = v.dy.mul(BASE_VIRTUAL_PRICE_PRECISION).div( v.baseVirtualPrice ); } // Update the balances array according to the calculated input and output amount { uint256 dyAdminFee = dyFee.mul(self.adminFee).div( FEE_DENOMINATOR ); dyAdminFee = dyAdminFee.div( v.tokenPrecisionMultipliers[v.metaIndexTo] ); self.balances[v.metaIndexFrom] = v .oldBalances[v.metaIndexFrom] .add(v.dx); self.balances[v.metaIndexTo] = v .oldBalances[v.metaIndexTo] .sub(v.dy) .sub(dyAdminFee); } if (tokenIndexTo >= baseLPTokenIndex) { // When swapping to a token that belongs to the base Swap, burn the LP token // and withdraw the desired token from the base pool uint256 oldBalance = v.tokenTo.balanceOf(address(this)); baseSwap.removeLiquidityOneToken( v.dy, tokenIndexTo - baseLPTokenIndex, 0, block.timestamp ); v.dy = v.tokenTo.balanceOf(address(this)) - oldBalance; } // Check the amount of token to send meets minDy require(v.dy >= minDy, "Swap didn't result in min tokens"); } else { // Both tokens are from the base Swap pool // Do a swap through the base Swap v.dy = v.tokenTo.balanceOf(address(this)); baseSwap.swap( tokenIndexFrom - baseLPTokenIndex, tokenIndexTo - baseLPTokenIndex, v.dx, minDy, block.timestamp ); v.dy = v.tokenTo.balanceOf(address(this)).sub(v.dy); } // Send the desired token to the caller v.tokenTo.safeTransfer(msg.sender, v.dy); emit TokenSwapUnderlying( msg.sender, dx, v.dy, tokenIndexFrom, tokenIndexTo ); return v.dy; } /** * @notice Add liquidity to the pool * @param self Swap struct to read from and write to * @param metaSwapStorage MetaSwap struct to read from and write to * @param amounts the amounts of each token to add, in their native precision * @param minToMint the minimum LP tokens adding this amount of liquidity * should mint, otherwise revert. Handy for front-running mitigation * allowed addresses. If the pool is not in the guarded launch phase, this parameter will be ignored. * @return amount of LP token user received */ function addLiquidity( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint256[] memory amounts, uint256 minToMint ) external returns (uint256) { IERC20[] memory pooledTokens = self.pooledTokens; require( amounts.length == pooledTokens.length, "Amounts must match pooled tokens" ); uint256[] memory fees = new uint256[](pooledTokens.length); // current state ManageLiquidityInfo memory v = ManageLiquidityInfo( 0, 0, 0, self.lpToken, 0, self._getAPrecise(), _updateBaseVirtualPrice(metaSwapStorage), self.tokenPrecisionMultipliers, self.balances ); v.totalSupply = v.lpToken.totalSupply(); if (v.totalSupply != 0) { v.d0 = SwapUtils.getD( _xp( v.newBalances, v.tokenPrecisionMultipliers, v.baseVirtualPrice ), v.preciseA ); } for (uint256 i = 0; i < pooledTokens.length; i++) { require( v.totalSupply != 0 || amounts[i] > 0, "Must supply all tokens in pool" ); // Transfer tokens first to see if a fee was charged on transfer if (amounts[i] != 0) { uint256 beforeBalance = pooledTokens[i].balanceOf( address(this) ); pooledTokens[i].safeTransferFrom( msg.sender, address(this), amounts[i] ); // Update the amounts[] with actual transfer amount amounts[i] = pooledTokens[i].balanceOf(address(this)).sub( beforeBalance ); } v.newBalances[i] = v.newBalances[i].add(amounts[i]); } // invariant after change v.d1 = SwapUtils.getD( _xp(v.newBalances, v.tokenPrecisionMultipliers, v.baseVirtualPrice), v.preciseA ); require(v.d1 > v.d0, "D should increase"); // updated to reflect fees and calculate the user's LP tokens v.d2 = v.d1; uint256 toMint; if (v.totalSupply != 0) { uint256 feePerToken = SwapUtils._feePerToken( self.swapFee, pooledTokens.length ); for (uint256 i = 0; i < pooledTokens.length; i++) { uint256 idealBalance = v.d1.mul(self.balances[i]).div(v.d0); fees[i] = feePerToken .mul(idealBalance.difference(v.newBalances[i])) .div(FEE_DENOMINATOR); self.balances[i] = v.newBalances[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); v.newBalances[i] = v.newBalances[i].sub(fees[i]); } v.d2 = SwapUtils.getD( _xp( v.newBalances, v.tokenPrecisionMultipliers, v.baseVirtualPrice ), v.preciseA ); toMint = v.d2.sub(v.d0).mul(v.totalSupply).div(v.d0); } else { // the initial depositor doesn't pay fees self.balances = v.newBalances; toMint = v.d1; } require(toMint >= minToMint, "Couldn't mint min requested"); // mint the user's LP tokens self.lpToken.mint(msg.sender, toMint); emit AddLiquidity( msg.sender, amounts, fees, v.d1, v.totalSupply.add(toMint) ); return toMint; } /** * @notice Remove liquidity from the pool all in one token. * @param self Swap struct to read from and write to * @param metaSwapStorage MetaSwap struct to read from and write to * @param tokenAmount the amount of the lp tokens to burn * @param tokenIndex the index of the token you want to receive * @param minAmount the minimum amount to withdraw, otherwise revert * @return amount chosen token that user received */ function removeLiquidityOneToken( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount ) external returns (uint256) { LPToken lpToken = self.lpToken; uint256 totalSupply = lpToken.totalSupply(); uint256 numTokens = self.pooledTokens.length; require(tokenAmount <= lpToken.balanceOf(msg.sender), ">LP.balanceOf"); require(tokenIndex < numTokens, "Token not found"); uint256 dyFee; uint256 dy; (dy, dyFee) = _calculateWithdrawOneToken( self, tokenAmount, tokenIndex, _updateBaseVirtualPrice(metaSwapStorage), totalSupply ); require(dy >= minAmount, "dy < minAmount"); // Update balances array self.balances[tokenIndex] = self.balances[tokenIndex].sub( dy.add(dyFee.mul(self.adminFee).div(FEE_DENOMINATOR)) ); // Burn the associated LP token from the caller and send the desired token lpToken.burnFrom(msg.sender, tokenAmount); self.pooledTokens[tokenIndex].safeTransfer(msg.sender, dy); emit RemoveLiquidityOne( msg.sender, tokenAmount, totalSupply, tokenIndex, dy ); return dy; } /** * @notice Remove liquidity from the pool, weighted differently than the * pool's current balances. * * @param self Swap struct to read from and write to * @param metaSwapStorage MetaSwap struct to read from and write to * @param amounts how much of each token to withdraw * @param maxBurnAmount the max LP token provider is willing to pay to * remove liquidity. Useful as a front-running mitigation. * @return actual amount of LP tokens burned in the withdrawal */ function removeLiquidityImbalance( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint256[] memory amounts, uint256 maxBurnAmount ) public returns (uint256) { // Using this struct to avoid stack too deep error ManageLiquidityInfo memory v = ManageLiquidityInfo( 0, 0, 0, self.lpToken, 0, self._getAPrecise(), _updateBaseVirtualPrice(metaSwapStorage), self.tokenPrecisionMultipliers, self.balances ); v.totalSupply = v.lpToken.totalSupply(); require( amounts.length == v.newBalances.length, "Amounts should match pool tokens" ); require(maxBurnAmount != 0, "Must burn more than 0"); uint256 feePerToken = SwapUtils._feePerToken( self.swapFee, v.newBalances.length ); // Calculate how much LPToken should be burned uint256[] memory fees = new uint256[](v.newBalances.length); { uint256[] memory balances1 = new uint256[](v.newBalances.length); v.d0 = SwapUtils.getD( _xp( v.newBalances, v.tokenPrecisionMultipliers, v.baseVirtualPrice ), v.preciseA ); for (uint256 i = 0; i < v.newBalances.length; i++) { balances1[i] = v.newBalances[i].sub( amounts[i], "Cannot withdraw more than available" ); } v.d1 = SwapUtils.getD( _xp(balances1, v.tokenPrecisionMultipliers, v.baseVirtualPrice), v.preciseA ); for (uint256 i = 0; i < v.newBalances.length; i++) { uint256 idealBalance = v.d1.mul(v.newBalances[i]).div(v.d0); uint256 difference = idealBalance.difference(balances1[i]); fees[i] = feePerToken.mul(difference).div(FEE_DENOMINATOR); self.balances[i] = balances1[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); balances1[i] = balances1[i].sub(fees[i]); } v.d2 = SwapUtils.getD( _xp(balances1, v.tokenPrecisionMultipliers, v.baseVirtualPrice), v.preciseA ); } uint256 tokenAmount = v.d0.sub(v.d2).mul(v.totalSupply).div(v.d0); require(tokenAmount != 0, "Burnt amount cannot be zero"); // Scale up by withdraw fee tokenAmount = tokenAmount.add(1); // Check for max burn amount require(tokenAmount <= maxBurnAmount, "tokenAmount > maxBurnAmount"); // Burn the calculated amount of LPToken from the caller and send the desired tokens v.lpToken.burnFrom(msg.sender, tokenAmount); for (uint256 i = 0; i < v.newBalances.length; i++) { self.pooledTokens[i].safeTransfer(msg.sender, amounts[i]); } emit RemoveLiquidityImbalance( msg.sender, amounts, fees, v.d1, v.totalSupply.sub(tokenAmount) ); return tokenAmount; } /** * @notice Determines if the stored value of base Swap's virtual price is expired. * If the last update was past the BASE_CACHE_EXPIRE_TIME, then update the stored value. * * @param metaSwapStorage MetaSwap struct to read from and write to * @return base Swap's virtual price */ function _updateBaseVirtualPrice(MetaSwap storage metaSwapStorage) internal returns (uint256) { if ( block.timestamp > metaSwapStorage.baseCacheLastUpdated + BASE_CACHE_EXPIRE_TIME ) { // When the cache is expired, update it uint256 baseVirtualPrice = ISwap(metaSwapStorage.baseSwap) .getVirtualPrice(); metaSwapStorage.baseVirtualPrice = baseVirtualPrice; metaSwapStorage.baseCacheLastUpdated = block.timestamp; return baseVirtualPrice; } else { return metaSwapStorage.baseVirtualPrice; } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import "../LPToken.sol"; import "../interfaces/ISwap.sol"; import "../interfaces/IMetaSwap.sol"; /** * @title MetaSwapDeposit * @notice This contract flattens the LP token in a MetaSwap pool for easier user access. MetaSwap must be * deployed before this contract can be initialized successfully. * * For example, suppose there exists a base Swap pool consisting of [DAI, USDC, USDT]. * Then a MetaSwap pool can be created with [sUSD, BaseSwapLPToken] to allow trades between either * the LP token or the underlying tokens and sUSD. * * MetaSwapDeposit flattens the LP token and remaps them to a single array, allowing users * to ignore the dependency on BaseSwapLPToken. Using the above example, MetaSwapDeposit can act * as a Swap containing [sUSD, DAI, USDC, USDT] tokens. */ contract MetaSwapDeposit is Initializable, ReentrancyGuardUpgradeable { using SafeERC20 for IERC20; using SafeMath for uint256; ISwap public baseSwap; IMetaSwap public metaSwap; IERC20[] public baseTokens; IERC20[] public metaTokens; IERC20[] public tokens; IERC20 public metaLPToken; uint256 constant MAX_UINT256 = 2**256 - 1; struct RemoveLiquidityImbalanceInfo { ISwap baseSwap; IMetaSwap metaSwap; IERC20 metaLPToken; uint8 baseLPTokenIndex; bool withdrawFromBase; uint256 leftoverMetaLPTokenAmount; } /** * @notice Sets the address for the base Swap contract, MetaSwap contract, and the * MetaSwap LP token contract. * @param _baseSwap the address of the base Swap contract * @param _metaSwap the address of the MetaSwap contract * @param _metaLPToken the address of the MetaSwap LP token contract */ function initialize( ISwap _baseSwap, IMetaSwap _metaSwap, IERC20 _metaLPToken ) external initializer { __ReentrancyGuard_init(); // Check and approve base level tokens to be deposited to the base Swap contract { uint8 i; for (; i < 32; i++) { try _baseSwap.getToken(i) returns (IERC20 token) { baseTokens.push(token); token.safeApprove(address(_baseSwap), MAX_UINT256); token.safeApprove(address(_metaSwap), MAX_UINT256); } catch { break; } } require(i > 1, "baseSwap must have at least 2 tokens"); } // Check and approve meta level tokens to be deposited to the MetaSwap contract IERC20 baseLPToken; { uint8 i; for (; i < 32; i++) { try _metaSwap.getToken(i) returns (IERC20 token) { baseLPToken = token; metaTokens.push(token); tokens.push(token); token.safeApprove(address(_metaSwap), MAX_UINT256); } catch { break; } } require(i > 1, "metaSwap must have at least 2 tokens"); } // Flatten baseTokens and append it to tokens array tokens[tokens.length - 1] = baseTokens[0]; for (uint8 i = 1; i < baseTokens.length; i++) { tokens.push(baseTokens[i]); } // Approve base Swap LP token to be burned by the base Swap contract for withdrawing baseLPToken.safeApprove(address(_baseSwap), MAX_UINT256); // Approve MetaSwap LP token to be burned by the MetaSwap contract for withdrawing _metaLPToken.safeApprove(address(_metaSwap), MAX_UINT256); // Initialize storage variables baseSwap = _baseSwap; metaSwap = _metaSwap; metaLPToken = _metaLPToken; } // Mutative functions /** * @notice Swap two underlying tokens using the meta pool and the base pool * @param tokenIndexFrom the token the user wants to swap from * @param tokenIndexTo the token the user wants to swap to * @param dx the amount of tokens the user wants to swap from * @param minDy the min amount the user would like to receive, or revert. * @param deadline latest timestamp to accept this transaction */ function swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external nonReentrant returns (uint256) { tokens[tokenIndexFrom].safeTransferFrom(msg.sender, address(this), dx); uint256 tokenToAmount = metaSwap.swapUnderlying( tokenIndexFrom, tokenIndexTo, dx, minDy, deadline ); tokens[tokenIndexTo].safeTransfer(msg.sender, tokenToAmount); return tokenToAmount; } /** * @notice Add liquidity to the pool with the given amounts of tokens * @param amounts the amounts of each token to add, in their native precision * @param minToMint the minimum LP tokens adding this amount of liquidity * should mint, otherwise revert. Handy for front-running mitigation * @param deadline latest timestamp to accept this transaction * @return amount of LP token user minted and received */ function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline ) external nonReentrant returns (uint256) { // Read to memory to save on gas IERC20[] memory memBaseTokens = baseTokens; IERC20[] memory memMetaTokens = metaTokens; uint256 baseLPTokenIndex = memMetaTokens.length - 1; require(amounts.length == memBaseTokens.length + baseLPTokenIndex); uint256 baseLPTokenAmount; { // Transfer base tokens from the caller and deposit to the base Swap pool uint256[] memory baseAmounts = new uint256[](memBaseTokens.length); bool shouldDepositBaseTokens; for (uint8 i = 0; i < memBaseTokens.length; i++) { IERC20 token = memBaseTokens[i]; uint256 depositAmount = amounts[baseLPTokenIndex + i]; if (depositAmount > 0) { token.safeTransferFrom( msg.sender, address(this), depositAmount ); baseAmounts[i] = token.balanceOf(address(this)); // account for any fees on transfer // if there are any base Swap level tokens, flag it for deposits shouldDepositBaseTokens = true; } } if (shouldDepositBaseTokens) { // Deposit any base Swap level tokens and receive baseLPToken baseLPTokenAmount = baseSwap.addLiquidity( baseAmounts, 0, deadline ); } } uint256 metaLPTokenAmount; { // Transfer remaining meta level tokens from the caller uint256[] memory metaAmounts = new uint256[](metaTokens.length); for (uint8 i = 0; i < baseLPTokenIndex; i++) { IERC20 token = memMetaTokens[i]; uint256 depositAmount = amounts[i]; if (depositAmount > 0) { token.safeTransferFrom( msg.sender, address(this), depositAmount ); metaAmounts[i] = token.balanceOf(address(this)); // account for any fees on transfer } } // Update the baseLPToken amount that will be deposited metaAmounts[baseLPTokenIndex] = baseLPTokenAmount; // Deposit the meta level tokens and the baseLPToken metaLPTokenAmount = metaSwap.addLiquidity( metaAmounts, minToMint, deadline ); } // Transfer the meta lp token to the caller metaLPToken.safeTransfer(msg.sender, metaLPTokenAmount); return metaLPTokenAmount; } /** * @notice Burn LP tokens to remove liquidity from the pool. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @dev Liquidity can always be removed, even when the pool is paused. * @param amount the amount of LP tokens to burn * @param minAmounts the minimum amounts of each token in the pool * acceptable for this burn. Useful as a front-running mitigation * @param deadline latest timestamp to accept this transaction * @return amounts of tokens user received */ function removeLiquidity( uint256 amount, uint256[] calldata minAmounts, uint256 deadline ) external nonReentrant returns (uint256[] memory) { IERC20[] memory memBaseTokens = baseTokens; IERC20[] memory memMetaTokens = metaTokens; uint256[] memory totalRemovedAmounts; { uint256 numOfAllTokens = memBaseTokens.length + memMetaTokens.length - 1; require(minAmounts.length == numOfAllTokens, "out of range"); totalRemovedAmounts = new uint256[](numOfAllTokens); } // Transfer meta lp token from the caller to this metaLPToken.safeTransferFrom(msg.sender, address(this), amount); uint256 baseLPTokenAmount; { // Remove liquidity from the MetaSwap pool uint256[] memory removedAmounts; uint256 baseLPTokenIndex = memMetaTokens.length - 1; { uint256[] memory metaMinAmounts = new uint256[]( memMetaTokens.length ); for (uint8 i = 0; i < baseLPTokenIndex; i++) { metaMinAmounts[i] = minAmounts[i]; } removedAmounts = metaSwap.removeLiquidity( amount, metaMinAmounts, deadline ); } // Send the meta level tokens to the caller for (uint8 i = 0; i < baseLPTokenIndex; i++) { totalRemovedAmounts[i] = removedAmounts[i]; memMetaTokens[i].safeTransfer(msg.sender, removedAmounts[i]); } baseLPTokenAmount = removedAmounts[baseLPTokenIndex]; // Remove liquidity from the base Swap pool { uint256[] memory baseMinAmounts = new uint256[]( memBaseTokens.length ); for (uint8 i = 0; i < baseLPTokenIndex; i++) { baseMinAmounts[i] = minAmounts[baseLPTokenIndex + i]; } removedAmounts = baseSwap.removeLiquidity( baseLPTokenAmount, baseMinAmounts, deadline ); } // Send the base level tokens to the caller for (uint8 i = 0; i < memBaseTokens.length; i++) { totalRemovedAmounts[baseLPTokenIndex + i] = removedAmounts[i]; memBaseTokens[i].safeTransfer(msg.sender, removedAmounts[i]); } } return totalRemovedAmounts; } /** * @notice Remove liquidity from the pool all in one token. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @param tokenAmount the amount of the token you want to receive * @param tokenIndex the index of the token you want to receive * @param minAmount the minimum amount to withdraw, otherwise revert * @param deadline latest timestamp to accept this transaction * @return amount of chosen token user received */ function removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline ) external nonReentrant returns (uint256) { uint8 baseLPTokenIndex = uint8(metaTokens.length - 1); uint8 baseTokensLength = uint8(baseTokens.length); // Transfer metaLPToken from the caller metaLPToken.safeTransferFrom(msg.sender, address(this), tokenAmount); IERC20 token; if (tokenIndex < baseLPTokenIndex) { // When the desired token is meta level token, we can just call `removeLiquidityOneToken` directly metaSwap.removeLiquidityOneToken( tokenAmount, tokenIndex, minAmount, deadline ); token = metaTokens[tokenIndex]; } else if (tokenIndex < baseLPTokenIndex + baseTokensLength) { // When the desired token is a base level token, we need to first withdraw via baseLPToken, then withdraw // the desired token from the base Swap contract. uint256 removedBaseLPTokenAmount = metaSwap.removeLiquidityOneToken( tokenAmount, baseLPTokenIndex, 0, deadline ); baseSwap.removeLiquidityOneToken( removedBaseLPTokenAmount, tokenIndex - baseLPTokenIndex, minAmount, deadline ); token = baseTokens[tokenIndex - baseLPTokenIndex]; } else { revert("out of range"); } uint256 amountWithdrawn = token.balanceOf(address(this)); token.safeTransfer(msg.sender, amountWithdrawn); return amountWithdrawn; } /** * @notice Remove liquidity from the pool, weighted differently than the * pool's current balances. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @param amounts how much of each token to withdraw * @param maxBurnAmount the max LP token provider is willing to pay to * remove liquidity. Useful as a front-running mitigation. * @param deadline latest timestamp to accept this transaction * @return amount of LP tokens burned */ function removeLiquidityImbalance( uint256[] calldata amounts, uint256 maxBurnAmount, uint256 deadline ) external nonReentrant returns (uint256) { IERC20[] memory memBaseTokens = baseTokens; IERC20[] memory memMetaTokens = metaTokens; uint256[] memory metaAmounts = new uint256[](memMetaTokens.length); uint256[] memory baseAmounts = new uint256[](memBaseTokens.length); require( amounts.length == memBaseTokens.length + memMetaTokens.length - 1, "out of range" ); RemoveLiquidityImbalanceInfo memory v = RemoveLiquidityImbalanceInfo( baseSwap, metaSwap, metaLPToken, uint8(metaAmounts.length - 1), false, 0 ); for (uint8 i = 0; i < v.baseLPTokenIndex; i++) { metaAmounts[i] = amounts[i]; } for (uint8 i = 0; i < baseAmounts.length; i++) { baseAmounts[i] = amounts[v.baseLPTokenIndex + i]; if (baseAmounts[i] > 0) { v.withdrawFromBase = true; } } // Calculate how much base LP token we need to get the desired amount of underlying tokens if (v.withdrawFromBase) { metaAmounts[v.baseLPTokenIndex] = v .baseSwap .calculateTokenAmount(baseAmounts, false) .mul(10005) .div(10000); } // Transfer MetaSwap LP token from the caller to this contract v.metaLPToken.safeTransferFrom( msg.sender, address(this), maxBurnAmount ); // Withdraw the paired meta level tokens and the base LP token from the MetaSwap pool uint256 burnedMetaLPTokenAmount = v.metaSwap.removeLiquidityImbalance( metaAmounts, maxBurnAmount, deadline ); v.leftoverMetaLPTokenAmount = maxBurnAmount.sub( burnedMetaLPTokenAmount ); // If underlying tokens are desired, withdraw them from the base Swap pool if (v.withdrawFromBase) { v.baseSwap.removeLiquidityImbalance( baseAmounts, metaAmounts[v.baseLPTokenIndex], deadline ); // Base Swap may require LESS base LP token than the amount we have // In that case, deposit it to the MetaSwap pool. uint256[] memory leftovers = new uint256[](metaAmounts.length); IERC20 baseLPToken = memMetaTokens[v.baseLPTokenIndex]; uint256 leftoverBaseLPTokenAmount = baseLPToken.balanceOf( address(this) ); if (leftoverBaseLPTokenAmount > 0) { leftovers[v.baseLPTokenIndex] = leftoverBaseLPTokenAmount; v.leftoverMetaLPTokenAmount = v.leftoverMetaLPTokenAmount.add( v.metaSwap.addLiquidity(leftovers, 0, deadline) ); } } // Transfer all withdrawn tokens to the caller for (uint8 i = 0; i < amounts.length; i++) { IERC20 token; if (i < v.baseLPTokenIndex) { token = memMetaTokens[i]; } else { token = memBaseTokens[i - v.baseLPTokenIndex]; } if (amounts[i] > 0) { token.safeTransfer(msg.sender, amounts[i]); } } // If there were any extra meta lp token, transfer them back to the caller as well if (v.leftoverMetaLPTokenAmount > 0) { v.metaLPToken.safeTransfer(msg.sender, v.leftoverMetaLPTokenAmount); } return maxBurnAmount - v.leftoverMetaLPTokenAmount; } // VIEW FUNCTIONS /** * @notice A simple method to calculate prices from deposits or * withdrawals, excluding fees but including slippage. This is * helpful as an input into the various "min" parameters on calls * to fight front-running. When withdrawing from the base pool in imbalanced * fashion, the recommended slippage setting is 0.2% or higher. * * @dev This shouldn't be used outside frontends for user estimates. * * @param amounts an array of token amounts to deposit or withdrawal, * corresponding to pooledTokens. The amount should be in each * pooled token's native precision. If a token charges a fee on transfers, * use the amount that gets transferred after the fee. * @param deposit whether this is a deposit or a withdrawal * @return token amount the user will receive */ function calculateTokenAmount(uint256[] calldata amounts, bool deposit) external view returns (uint256) { uint256[] memory metaAmounts = new uint256[](metaTokens.length); uint256[] memory baseAmounts = new uint256[](baseTokens.length); uint256 baseLPTokenIndex = metaAmounts.length - 1; for (uint8 i = 0; i < baseLPTokenIndex; i++) { metaAmounts[i] = amounts[i]; } for (uint8 i = 0; i < baseAmounts.length; i++) { baseAmounts[i] = amounts[baseLPTokenIndex + i]; } uint256 baseLPTokenAmount = baseSwap.calculateTokenAmount( baseAmounts, deposit ); metaAmounts[baseLPTokenIndex] = baseLPTokenAmount; return metaSwap.calculateTokenAmount(metaAmounts, deposit); } /** * @notice A simple method to calculate amount of each underlying * tokens that is returned upon burning given amount of LP tokens * @param amount the amount of LP tokens that would be burned on withdrawal * @return array of token balances that the user will receive */ function calculateRemoveLiquidity(uint256 amount) external view returns (uint256[] memory) { uint256[] memory metaAmounts = metaSwap.calculateRemoveLiquidity( amount ); uint8 baseLPTokenIndex = uint8(metaAmounts.length - 1); uint256[] memory baseAmounts = baseSwap.calculateRemoveLiquidity( metaAmounts[baseLPTokenIndex] ); uint256[] memory totalAmounts = new uint256[]( baseLPTokenIndex + baseAmounts.length ); for (uint8 i = 0; i < baseLPTokenIndex; i++) { totalAmounts[i] = metaAmounts[i]; } for (uint8 i = 0; i < baseAmounts.length; i++) { totalAmounts[baseLPTokenIndex + i] = baseAmounts[i]; } return totalAmounts; } /** * @notice Calculate the amount of underlying token available to withdraw * when withdrawing via only single token * @param tokenAmount the amount of LP token to burn * @param tokenIndex index of which token will be withdrawn * @return availableTokenAmount calculated amount of underlying token * available to withdraw */ function calculateRemoveLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256) { uint8 baseLPTokenIndex = uint8(metaTokens.length - 1); if (tokenIndex < baseLPTokenIndex) { return metaSwap.calculateRemoveLiquidityOneToken( tokenAmount, tokenIndex ); } else { uint256 baseLPTokenAmount = metaSwap .calculateRemoveLiquidityOneToken( tokenAmount, baseLPTokenIndex ); return baseSwap.calculateRemoveLiquidityOneToken( baseLPTokenAmount, tokenIndex - baseLPTokenIndex ); } } /** * @notice Returns the address of the pooled token at given index. Reverts if tokenIndex is out of range. * This is a flattened representation of the pooled tokens. * @param index the index of the token * @return address of the token at given index */ function getToken(uint8 index) external view returns (IERC20) { require(index < tokens.length, "index out of range"); return tokens[index]; } /** * @notice Calculate amount of tokens you receive on swap * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell. If the token charges * a fee on transfers, use the amount that gets transferred after the fee. * @return amount of tokens the user will receive */ function calculateSwap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256) { return metaSwap.calculateSwapUnderlying(tokenIndexFrom, tokenIndexTo, dx); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "./ISwap.sol"; interface IMetaSwap { // pool data view functions function getA() external view returns (uint256); function getToken(uint8 index) external view returns (IERC20); function getTokenIndex(address tokenAddress) external view returns (uint8); function getTokenBalance(uint8 index) external view returns (uint256); function getVirtualPrice() external view returns (uint256); function isGuarded() external view returns (bool); // min return calculation functions function calculateSwap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256); function calculateSwapUnderlying( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256); function calculateTokenAmount(uint256[] calldata amounts, bool deposit) external view returns (uint256); function calculateRemoveLiquidity(uint256 amount) external view returns (uint256[] memory); function calculateRemoveLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256 availableTokenAmount); // state modifying functions function initialize( IERC20[] memory _pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 _a, uint256 _fee, uint256 _adminFee, address lpTokenTargetAddress ) external; function initializeMetaSwap( IERC20[] memory _pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 _a, uint256 _fee, uint256 _adminFee, address lpTokenTargetAddress, ISwap baseSwap ) external; function swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external returns (uint256); function swapUnderlying( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external returns (uint256); function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline ) external returns (uint256); function removeLiquidity( uint256 amount, uint256[] calldata minAmounts, uint256 deadline ) external returns (uint256[] memory); function removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline ) external returns (uint256); function removeLiquidityImbalance( uint256[] calldata amounts, uint256 maxBurnAmount, uint256 deadline ) external returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/proxy/Clones.sol"; import "./interfaces/ISwap.sol"; import "./interfaces/IMetaSwap.sol"; contract SwapDeployer is Ownable { event NewSwapPool( address indexed deployer, address swapAddress, IERC20[] pooledTokens ); event NewClone(address indexed target, address cloneAddress); constructor() public Ownable() {} function clone(address target) external returns (address) { address newClone = _clone(target); emit NewClone(target, newClone); return newClone; } function _clone(address target) internal returns (address) { return Clones.clone(target); } function deploy( address swapAddress, IERC20[] memory _pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 _a, uint256 _fee, uint256 _adminFee, address lpTokenTargetAddress ) external returns (address) { address swapClone = _clone(swapAddress); ISwap(swapClone).initialize( _pooledTokens, decimals, lpTokenName, lpTokenSymbol, _a, _fee, _adminFee, lpTokenTargetAddress ); Ownable(swapClone).transferOwnership(owner()); emit NewSwapPool(msg.sender, swapClone, _pooledTokens); return swapClone; } function deployMetaSwap( address metaSwapAddress, IERC20[] memory _pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 _a, uint256 _fee, uint256 _adminFee, address lpTokenTargetAddress, ISwap baseSwap ) external returns (address) { address metaSwapClone = _clone(metaSwapAddress); IMetaSwap(metaSwapClone).initializeMetaSwap( _pooledTokens, decimals, lpTokenName, lpTokenSymbol, _a, _fee, _adminFee, lpTokenTargetAddress, baseSwap ); Ownable(metaSwapClone).transferOwnership(owner()); emit NewSwapPool(msg.sender, metaSwapClone, _pooledTokens); return metaSwapClone; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract 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 () internal { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "synthetix/contracts/interfaces/ISynthetix.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; import "../interfaces/ISwap.sol"; /** * @title SynthSwapper * @notice Replacement of Virtual Synths in favor of gas savings. Allows swapping synths via the Synthetix protocol * or Saddle's pools. The `Bridge.sol` contract will deploy minimal clones of this contract upon initiating * any cross-asset swaps. */ contract SynthSwapper is Initializable { using SafeERC20 for IERC20; address payable owner; // SYNTHETIX points to `ProxyERC20` (0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F). // This contract is a proxy of `Synthetix` and is used to exchange synths. ISynthetix public constant SYNTHETIX = ISynthetix(0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F); // "SADDLE" in bytes32 form bytes32 public constant TRACKING = 0x534144444c450000000000000000000000000000000000000000000000000000; /** * @notice Initializes the contract when deploying this directly. This prevents * others from calling initialize() on the target contract and setting themself as the owner. */ constructor() public { initialize(); } /** * @notice This modifier checks if the caller is the owner */ modifier onlyOwner() { require(msg.sender == owner, "is not owner"); _; } /** * @notice Sets the `owner` as the caller of this function */ function initialize() public initializer { require(owner == address(0), "owner already set"); owner = msg.sender; } /** * @notice Swaps the synth to another synth via the Synthetix protocol. * @param sourceKey currency key of the source synth * @param synthAmount amount of the synth to swap * @param destKey currency key of the destination synth * @return amount of the destination synth received */ function swapSynth( bytes32 sourceKey, uint256 synthAmount, bytes32 destKey ) external onlyOwner returns (uint256) { return SYNTHETIX.exchangeWithTracking( sourceKey, synthAmount, destKey, msg.sender, TRACKING ); } /** * @notice Approves the given `tokenFrom` and swaps it to another token via the given `swap` pool. * @param swap the address of a pool to swap through * @param tokenFrom the address of the stored synth * @param tokenFromIndex the index of the token to swap from * @param tokenToIndex the token the user wants to swap to * @param tokenFromAmount the amount of the token to swap * @param minAmount the min amount the user would like to receive, or revert. * @param deadline latest timestamp to accept this transaction * @param recipient the address of the recipient */ function swapSynthToToken( ISwap swap, IERC20 tokenFrom, uint8 tokenFromIndex, uint8 tokenToIndex, uint256 tokenFromAmount, uint256 minAmount, uint256 deadline, address recipient ) external onlyOwner returns (IERC20, uint256) { tokenFrom.approve(address(swap), tokenFromAmount); swap.swap( tokenFromIndex, tokenToIndex, tokenFromAmount, minAmount, deadline ); IERC20 tokenTo = swap.getToken(tokenToIndex); uint256 balance = tokenTo.balanceOf(address(this)); tokenTo.safeTransfer(recipient, balance); return (tokenTo, balance); } /** * @notice Withdraws the given amount of `token` to the `recipient`. * @param token the address of the token to withdraw * @param recipient the address of the account to receive the token * @param withdrawAmount the amount of the token to withdraw * @param shouldDestroy whether this contract should be destroyed after this call */ function withdraw( IERC20 token, address recipient, uint256 withdrawAmount, bool shouldDestroy ) external onlyOwner { token.safeTransfer(recipient, withdrawAmount); if (shouldDestroy) { _destroy(); } } /** * @notice Destroys this contract. Only owner can call this function. */ function destroy() external onlyOwner { _destroy(); } function _destroy() internal { selfdestruct(msg.sender); } }
pragma solidity >=0.4.24; import "./ISynth.sol"; import "./IVirtualSynth.sol"; // https://docs.synthetix.io/contracts/source/interfaces/isynthetix interface ISynthetix { // Views function anySynthOrSNXRateIsInvalid() external view returns (bool anyRateInvalid); function availableCurrencyKeys() external view returns (bytes32[] memory); function availableSynthCount() external view returns (uint); function availableSynths(uint index) external view returns (ISynth); function collateral(address account) external view returns (uint); function collateralisationRatio(address issuer) external view returns (uint); function debtBalanceOf(address issuer, bytes32 currencyKey) external view returns (uint); function isWaitingPeriod(bytes32 currencyKey) external view returns (bool); function maxIssuableSynths(address issuer) external view returns (uint maxIssuable); function remainingIssuableSynths(address issuer) external view returns ( uint maxIssuable, uint alreadyIssued, uint totalSystemDebt ); function synths(bytes32 currencyKey) external view returns (ISynth); function synthsByAddress(address synthAddress) external view returns (bytes32); function totalIssuedSynths(bytes32 currencyKey) external view returns (uint); function totalIssuedSynthsExcludeEtherCollateral(bytes32 currencyKey) external view returns (uint); function transferableSynthetix(address account) external view returns (uint transferable); // Mutative Functions function burnSynths(uint amount) external; function burnSynthsOnBehalf(address burnForAddress, uint amount) external; function burnSynthsToTarget() external; function burnSynthsToTargetOnBehalf(address burnForAddress) external; function exchange( bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey ) external returns (uint amountReceived); function exchangeOnBehalf( address exchangeForAddress, bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey ) external returns (uint amountReceived); function exchangeWithTracking( bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, address originator, bytes32 trackingCode ) external returns (uint amountReceived); function exchangeOnBehalfWithTracking( address exchangeForAddress, bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, address originator, bytes32 trackingCode ) external returns (uint amountReceived); function exchangeWithVirtual( bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, bytes32 trackingCode ) external returns (uint amountReceived, IVirtualSynth vSynth); function issueMaxSynths() external; function issueMaxSynthsOnBehalf(address issueForAddress) external; function issueSynths(uint amount) external; function issueSynthsOnBehalf(address issueForAddress, uint amount) external; function mint() external returns (bool); function settle(bytes32 currencyKey) external returns ( uint reclaimed, uint refunded, uint numEntries ); // Liquidations function liquidateDelinquentAccount(address account, uint susdAmount) external returns (bool); // Restricted Functions function mintSecondary(address account, uint amount) external; function mintSecondaryRewards(uint amount) external; function burnSecondary(address account, uint amount) external; }
pragma solidity >=0.4.24; // https://docs.synthetix.io/contracts/source/interfaces/isynth interface ISynth { // Views function currencyKey() external view returns (bytes32); function transferableSynths(address account) external view returns (uint); // Mutative functions function transferAndSettle(address to, uint value) external returns (bool); function transferFromAndSettle( address from, address to, uint value ) external returns (bool); // Restricted: used internally to Synthetix function burn(address account, uint amount) external; function issue(address account, uint amount) external; }
pragma solidity >=0.4.24; import "./ISynth.sol"; interface IVirtualSynth { // Views function balanceOfUnderlying(address account) external view returns (uint); function rate() external view returns (uint); function readyToSettle() external view returns (bool); function secsLeftInWaitingPeriod() external view returns (uint); function settled() external view returns (bool); function synth() external view returns (ISynth); // Mutative functions function settle(address account) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20BurnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "./interfaces/ISwapV1.sol"; /** * @title Liquidity Provider Token * @notice This token is an ERC20 detailed token with added capability to be minted by the owner. * It is used to represent user's shares when providing liquidity to swap contracts. * @dev Only Swap contracts should initialize and own LPToken contracts. */ contract LPTokenV1 is ERC20BurnableUpgradeable, OwnableUpgradeable { using SafeMathUpgradeable for uint256; /** * @notice Initializes this LPToken contract with the given name and symbol * @dev The caller of this function will become the owner. A Swap contract should call this * in its initializer function. * @param name name of this token * @param symbol symbol of this token */ function initialize(string memory name, string memory symbol) external initializer returns (bool) { __Context_init_unchained(); __ERC20_init_unchained(name, symbol); __Ownable_init_unchained(); return true; } /** * @notice Mints the given amount of LPToken to the recipient. * @dev only owner can call this mint function * @param recipient address of account to receive the tokens * @param amount amount of tokens to mint */ function mint(address recipient, uint256 amount) external onlyOwner { require(amount != 0, "LPToken: cannot mint 0"); _mint(recipient, amount); } /** * @dev Overrides ERC20._beforeTokenTransfer() which get called on every transfers including * minting and burning. This ensures that Swap.updateUserWithdrawFees are called everytime. * This assumes the owner is set to a Swap contract's address. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual override(ERC20Upgradeable) { super._beforeTokenTransfer(from, to, amount); require(to != address(this), "LPToken: cannot send to itself"); ISwapV1(owner()).updateUserWithdrawFee(to, amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "./IAllowlist.sol"; interface ISwapV1 { // pool data view functions function getA() external view returns (uint256); function getAllowlist() external view returns (IAllowlist); function getToken(uint8 index) external view returns (IERC20); function getTokenIndex(address tokenAddress) external view returns (uint8); function getTokenBalance(uint8 index) external view returns (uint256); function getVirtualPrice() external view returns (uint256); function isGuarded() external view returns (bool); // min return calculation functions function calculateSwap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256); function calculateTokenAmount( address account, uint256[] calldata amounts, bool deposit ) external view returns (uint256); function calculateRemoveLiquidity(address account, uint256 amount) external view returns (uint256[] memory); function calculateRemoveLiquidityOneToken( address account, uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256 availableTokenAmount); // state modifying functions function initialize( IERC20[] memory pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 a, uint256 fee, uint256 adminFee, uint256 withdrawFee, address lpTokenTargetAddress ) external; function swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external returns (uint256); function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline ) external returns (uint256); function removeLiquidity( uint256 amount, uint256[] calldata minAmounts, uint256 deadline ) external returns (uint256[] memory); function removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline ) external returns (uint256); function removeLiquidityImbalance( uint256[] calldata amounts, uint256 maxBurnAmount, uint256 deadline ) external returns (uint256); // withdraw fee update function function updateUserWithdrawFee(address recipient, uint256 transferAmount) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/proxy/Clones.sol"; import "./interfaces/ISwapV1.sol"; contract SwapDeployerV1 is Ownable { event NewSwapPool( address indexed deployer, address swapAddress, IERC20[] pooledTokens ); constructor() public Ownable() {} function deploy( address swapAddress, IERC20[] memory _pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 _a, uint256 _fee, uint256 _adminFee, uint256 _withdrawFee, address lpTokenTargetAddress ) external returns (address) { address swapClone = Clones.clone(swapAddress); ISwapV1(swapClone).initialize( _pooledTokens, decimals, lpTokenName, lpTokenSymbol, _a, _fee, _adminFee, _withdrawFee, lpTokenTargetAddress ); Ownable(swapClone).transferOwnership(owner()); emit NewSwapPool(msg.sender, swapClone, _pooledTokens); return swapClone; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/proxy/Clones.sol"; import "synthetix/contracts/interfaces/IAddressResolver.sol"; import "synthetix/contracts/interfaces/IExchanger.sol"; import "synthetix/contracts/interfaces/IExchangeRates.sol"; import "../interfaces/ISwap.sol"; import "./SynthSwapper.sol"; contract Proxy { address public target; } contract Target { address public proxy; } /** * @title Bridge * @notice This contract is responsible for cross-asset swaps using the Synthetix protocol as the bridging exchange. * There are three types of supported cross-asset swaps, tokenToSynth, synthToToken, and tokenToToken. * * 1) tokenToSynth * Swaps a supported token in a saddle pool to any synthetic asset (e.g. tBTC -> sAAVE). * * 2) synthToToken * Swaps any synthetic asset to a suported token in a saddle pool (e.g. sDEFI -> USDC). * * 3) tokenToToken * Swaps a supported token in a saddle pool to one in another pool (e.g. renBTC -> DAI). * * Due to the settlement periods of synthetic assets, the users must wait until the trades can be completed. * Users will receive an ERC721 token that represents pending cross-asset swap. Once the waiting period is over, * the trades can be settled and completed by calling the `completeToSynth` or the `completeToToken` function. * In the cases of pending `synthToToken` or `tokenToToken` swaps, the owners of the pending swaps can also choose * to withdraw the bridging synthetic assets instead of completing the swap. */ contract Bridge is ERC721 { using SafeMath for uint256; using SafeERC20 for IERC20; event SynthIndex( address indexed swap, uint8 synthIndex, bytes32 currencyKey, address synthAddress ); event TokenToSynth( address indexed requester, uint256 indexed itemId, ISwap swapPool, uint8 tokenFromIndex, uint256 tokenFromInAmount, bytes32 synthToKey ); event SynthToToken( address indexed requester, uint256 indexed itemId, ISwap swapPool, bytes32 synthFromKey, uint256 synthFromInAmount, uint8 tokenToIndex ); event TokenToToken( address indexed requester, uint256 indexed itemId, ISwap[2] swapPools, uint8 tokenFromIndex, uint256 tokenFromAmount, uint8 tokenToIndex ); event Settle( address indexed requester, uint256 indexed itemId, IERC20 settleFrom, uint256 settleFromAmount, IERC20 settleTo, uint256 settleToAmount, bool isFinal ); event Withdraw( address indexed requester, uint256 indexed itemId, IERC20 synth, uint256 synthAmount, bool isFinal ); // The addresses for all Synthetix contracts can be found in the below URL. // https://docs.synthetix.io/addresses/#mainnet-contracts // // Since the Synthetix protocol is upgradable, we must use the proxy pairs of each contract such that // the composability is not broken after the protocol upgrade. // // SYNTHETIX_RESOLVER points to `ReadProxyAddressResolver` (0x4E3b31eB0E5CB73641EE1E65E7dCEFe520bA3ef2). // This contract is a read proxy of `AddressResolver` which is responsible for storing the addresses of the contracts // used by the Synthetix protocol. IAddressResolver public constant SYNTHETIX_RESOLVER = IAddressResolver(0x4E3b31eB0E5CB73641EE1E65E7dCEFe520bA3ef2); // EXCHANGER points to `Exchanger`. There is no proxy pair for this contract so we need to update this variable // when the protocol is upgraded. This contract is used to settle synths held by SynthSwapper. IExchanger public exchanger; // CONSTANTS // Available types of cross-asset swaps enum PendingSwapType { Null, TokenToSynth, SynthToToken, TokenToToken } uint256 public constant MAX_UINT256 = 2**256 - 1; uint8 public constant MAX_UINT8 = 2**8 - 1; bytes32 public constant EXCHANGE_RATES_NAME = "ExchangeRates"; bytes32 public constant EXCHANGER_NAME = "Exchanger"; address public immutable SYNTH_SWAPPER_MASTER; // MAPPINGS FOR STORING PENDING SETTLEMENTS // The below two mappings never share the same key. mapping(uint256 => PendingToSynthSwap) public pendingToSynthSwaps; mapping(uint256 => PendingToTokenSwap) public pendingToTokenSwaps; uint256 public pendingSwapsLength; mapping(uint256 => PendingSwapType) private pendingSwapType; // MAPPINGS FOR STORING SYNTH INFO mapping(address => SwapContractInfo) private swapContracts; // Structs holding information about pending settlements struct PendingToSynthSwap { SynthSwapper swapper; bytes32 synthKey; } struct PendingToTokenSwap { SynthSwapper swapper; bytes32 synthKey; ISwap swap; uint8 tokenToIndex; } struct SwapContractInfo { // index of the supported synth + 1 uint8 synthIndexPlusOne; // address of the supported synth address synthAddress; // bytes32 key of the supported synth bytes32 synthKey; // array of tokens supported by the contract IERC20[] tokens; } /** * @notice Deploys this contract and initializes the master version of the SynthSwapper contract. The address to * the Synthetix protocol's Exchanger contract is also set on deployment. */ constructor(address synthSwapperAddress) public ERC721("Saddle Cross-Asset Swap", "SaddleSynthSwap") { SYNTH_SWAPPER_MASTER = synthSwapperAddress; updateExchangerCache(); } /** * @notice Returns the address of the proxy contract targeting the synthetic asset with the given `synthKey`. * @param synthKey the currency key of the synth * @return address of the proxy contract */ function getProxyAddressFromTargetSynthKey(bytes32 synthKey) public view returns (IERC20) { return IERC20(Target(SYNTHETIX_RESOLVER.getSynth(synthKey)).proxy()); } /** * @notice Returns various information of a pending swap represented by the given `itemId`. Information includes * the type of the pending swap, the number of seconds left until it can be settled, the address and the balance * of the synth this swap currently holds, and the address of the destination token. * @param itemId ID of the pending swap * @return swapType the type of the pending virtual swap, * secsLeft number of seconds left until this swap can be settled, * synth address of the synth this swap uses, * synthBalance amount of the synth this swap holds, * tokenTo the address of the destination token */ function getPendingSwapInfo(uint256 itemId) external view returns ( PendingSwapType swapType, uint256 secsLeft, address synth, uint256 synthBalance, address tokenTo ) { swapType = pendingSwapType[itemId]; require(swapType != PendingSwapType.Null, "invalid itemId"); SynthSwapper synthSwapper; bytes32 synthKey; if (swapType == PendingSwapType.TokenToSynth) { synthSwapper = pendingToSynthSwaps[itemId].swapper; synthKey = pendingToSynthSwaps[itemId].synthKey; synth = address(getProxyAddressFromTargetSynthKey(synthKey)); tokenTo = synth; } else { PendingToTokenSwap memory pendingToTokenSwap = pendingToTokenSwaps[ itemId ]; synthSwapper = pendingToTokenSwap.swapper; synthKey = pendingToTokenSwap.synthKey; synth = address(getProxyAddressFromTargetSynthKey(synthKey)); tokenTo = address( swapContracts[address(pendingToTokenSwap.swap)].tokens[ pendingToTokenSwap.tokenToIndex ] ); } secsLeft = exchanger.maxSecsLeftInWaitingPeriod( address(synthSwapper), synthKey ); synthBalance = IERC20(synth).balanceOf(address(synthSwapper)); } // Settles the synth only. function _settle(address synthOwner, bytes32 synthKey) internal { // Settle synth exchanger.settle(synthOwner, synthKey); } /** * @notice Settles and withdraws the synthetic asset without swapping it to a token in a Saddle pool. Only the owner * of the ERC721 token of `itemId` can call this function. Reverts if the given `itemId` does not represent a * `synthToToken` or a `tokenToToken` swap. * @param itemId ID of the pending swap * @param amount the amount of the synth to withdraw */ function withdraw(uint256 itemId, uint256 amount) external { address nftOwner = ownerOf(itemId); require(nftOwner == msg.sender, "not owner"); require( pendingSwapType[itemId] > PendingSwapType.TokenToSynth, "invalid itemId" ); PendingToTokenSwap memory pendingToTokenSwap = pendingToTokenSwaps[ itemId ]; _settle( address(pendingToTokenSwap.swapper), pendingToTokenSwap.synthKey ); IERC20 synth = getProxyAddressFromTargetSynthKey( pendingToTokenSwap.synthKey ); bool shouldDestroy; if (amount >= synth.balanceOf(address(pendingToTokenSwap.swapper))) { _burn(itemId); delete pendingToTokenSwaps[itemId]; delete pendingSwapType[itemId]; shouldDestroy = true; } pendingToTokenSwap.swapper.withdraw( synth, nftOwner, amount, shouldDestroy ); emit Withdraw(msg.sender, itemId, synth, amount, shouldDestroy); } /** * @notice Completes the pending `tokenToSynth` swap by settling and withdrawing the synthetic asset. * Reverts if the given `itemId` does not represent a `tokenToSynth` swap. * @param itemId ERC721 token ID representing a pending `tokenToSynth` swap */ function completeToSynth(uint256 itemId) external { address nftOwner = ownerOf(itemId); require(nftOwner == msg.sender, "not owner"); require( pendingSwapType[itemId] == PendingSwapType.TokenToSynth, "invalid itemId" ); PendingToSynthSwap memory pendingToSynthSwap = pendingToSynthSwaps[ itemId ]; _settle( address(pendingToSynthSwap.swapper), pendingToSynthSwap.synthKey ); IERC20 synth = getProxyAddressFromTargetSynthKey( pendingToSynthSwap.synthKey ); // Burn the corresponding ERC721 token and delete storage for gas _burn(itemId); delete pendingToTokenSwaps[itemId]; delete pendingSwapType[itemId]; // After settlement, withdraw the synth and send it to the recipient uint256 synthBalance = synth.balanceOf( address(pendingToSynthSwap.swapper) ); pendingToSynthSwap.swapper.withdraw( synth, nftOwner, synthBalance, true ); emit Settle( msg.sender, itemId, synth, synthBalance, synth, synthBalance, true ); } /** * @notice Calculates the expected amount of the token to receive on calling `completeToToken()` with * the given `swapAmount`. * @param itemId ERC721 token ID representing a pending `SynthToToken` or `TokenToToken` swap * @param swapAmount the amount of bridging synth to swap from * @return expected amount of the token the user will receive */ function calcCompleteToToken(uint256 itemId, uint256 swapAmount) external view returns (uint256) { require( pendingSwapType[itemId] > PendingSwapType.TokenToSynth, "invalid itemId" ); PendingToTokenSwap memory pendingToTokenSwap = pendingToTokenSwaps[ itemId ]; return pendingToTokenSwap.swap.calculateSwap( getSynthIndex(pendingToTokenSwap.swap), pendingToTokenSwap.tokenToIndex, swapAmount ); } /** * @notice Completes the pending `SynthToToken` or `TokenToToken` swap by settling the bridging synth and swapping * it to the desired token. Only the owners of the pending swaps can call this function. * @param itemId ERC721 token ID representing a pending `SynthToToken` or `TokenToToken` swap * @param swapAmount the amount of bridging synth to swap from * @param minAmount the minimum amount of the token to receive - reverts if this amount is not reached * @param deadline the timestamp representing the deadline for this transaction - reverts if deadline is not met */ function completeToToken( uint256 itemId, uint256 swapAmount, uint256 minAmount, uint256 deadline ) external { require(swapAmount != 0, "amount must be greater than 0"); address nftOwner = ownerOf(itemId); require(msg.sender == nftOwner, "must own itemId"); require( pendingSwapType[itemId] > PendingSwapType.TokenToSynth, "invalid itemId" ); PendingToTokenSwap memory pendingToTokenSwap = pendingToTokenSwaps[ itemId ]; _settle( address(pendingToTokenSwap.swapper), pendingToTokenSwap.synthKey ); IERC20 synth = getProxyAddressFromTargetSynthKey( pendingToTokenSwap.synthKey ); bool shouldDestroyClone; if ( swapAmount >= synth.balanceOf(address(pendingToTokenSwap.swapper)) ) { _burn(itemId); delete pendingToTokenSwaps[itemId]; delete pendingSwapType[itemId]; shouldDestroyClone = true; } // Try swapping the synth to the desired token via the stored swap pool contract // If the external call succeeds, send the token to the owner of token with itemId. (IERC20 tokenTo, uint256 amountOut) = pendingToTokenSwap .swapper .swapSynthToToken( pendingToTokenSwap.swap, synth, getSynthIndex(pendingToTokenSwap.swap), pendingToTokenSwap.tokenToIndex, swapAmount, minAmount, deadline, nftOwner ); if (shouldDestroyClone) { pendingToTokenSwap.swapper.destroy(); } emit Settle( msg.sender, itemId, synth, swapAmount, tokenTo, amountOut, shouldDestroyClone ); } // Add the given pending synth settlement struct to the list function _addToPendingSynthSwapList( PendingToSynthSwap memory pendingToSynthSwap ) internal returns (uint256) { require( pendingSwapsLength < MAX_UINT256, "pendingSwapsLength reached max size" ); pendingToSynthSwaps[pendingSwapsLength] = pendingToSynthSwap; return pendingSwapsLength++; } // Add the given pending synth to token settlement struct to the list function _addToPendingSynthToTokenSwapList( PendingToTokenSwap memory pendingToTokenSwap ) internal returns (uint256) { require( pendingSwapsLength < MAX_UINT256, "pendingSwapsLength reached max size" ); pendingToTokenSwaps[pendingSwapsLength] = pendingToTokenSwap; return pendingSwapsLength++; } /** * @notice Calculates the expected amount of the desired synthetic asset the caller will receive after completing * a `TokenToSynth` swap with the given parameters. This calculation does not consider the settlement periods. * @param swap the address of a Saddle pool to use to swap the given token to a bridging synth * @param tokenFromIndex the index of the token to swap from * @param synthOutKey the currency key of the desired synthetic asset * @param tokenInAmount the amount of the token to swap form * @return the expected amount of the desired synth */ function calcTokenToSynth( ISwap swap, uint8 tokenFromIndex, bytes32 synthOutKey, uint256 tokenInAmount ) external view returns (uint256) { uint8 mediumSynthIndex = getSynthIndex(swap); uint256 expectedMediumSynthAmount = swap.calculateSwap( tokenFromIndex, mediumSynthIndex, tokenInAmount ); bytes32 mediumSynthKey = getSynthKey(swap); IExchangeRates exchangeRates = IExchangeRates( SYNTHETIX_RESOLVER.getAddress(EXCHANGE_RATES_NAME) ); return exchangeRates.effectiveValue( mediumSynthKey, expectedMediumSynthAmount, synthOutKey ); } /** * @notice Initiates a cross-asset swap from a token supported in the `swap` pool to any synthetic asset. * The caller will receive an ERC721 token representing their ownership of the pending cross-asset swap. * @param swap the address of a Saddle pool to use to swap the given token to a bridging synth * @param tokenFromIndex the index of the token to swap from * @param synthOutKey the currency key of the desired synthetic asset * @param tokenInAmount the amount of the token to swap form * @param minAmount the amount of the token to swap form * @return ID of the ERC721 token sent to the caller */ function tokenToSynth( ISwap swap, uint8 tokenFromIndex, bytes32 synthOutKey, uint256 tokenInAmount, uint256 minAmount ) external returns (uint256) { require(tokenInAmount != 0, "amount must be greater than 0"); // Create a SynthSwapper clone SynthSwapper synthSwapper = SynthSwapper( Clones.clone(SYNTH_SWAPPER_MASTER) ); synthSwapper.initialize(); // Add the synthswapper to the pending settlement list uint256 itemId = _addToPendingSynthSwapList( PendingToSynthSwap(synthSwapper, synthOutKey) ); pendingSwapType[itemId] = PendingSwapType.TokenToSynth; // Mint an ERC721 token that represents ownership of the pending synth settlement to msg.sender _mint(msg.sender, itemId); // Transfer token from msg.sender IERC20 tokenFrom = swapContracts[address(swap)].tokens[tokenFromIndex]; // revert when token not found in swap pool tokenFrom.safeTransferFrom(msg.sender, address(this), tokenInAmount); tokenInAmount = tokenFrom.balanceOf(address(this)); // Swap the synth to the medium synth uint256 mediumSynthAmount = swap.swap( tokenFromIndex, getSynthIndex(swap), tokenInAmount, 0, block.timestamp ); // Swap synths via Synthetix network IERC20(getSynthAddress(swap)).safeTransfer( address(synthSwapper), mediumSynthAmount ); require( synthSwapper.swapSynth( getSynthKey(swap), mediumSynthAmount, synthOutKey ) >= minAmount, "minAmount not reached" ); // Emit TokenToSynth event with relevant data emit TokenToSynth( msg.sender, itemId, swap, tokenFromIndex, tokenInAmount, synthOutKey ); return (itemId); } /** * @notice Calculates the expected amount of the desired token the caller will receive after completing * a `SynthToToken` swap with the given parameters. This calculation does not consider the settlement periods or * any potential changes of the `swap` pool composition. * @param swap the address of a Saddle pool to use to swap the given token to a bridging synth * @param synthInKey the currency key of the synth to swap from * @param tokenToIndex the index of the token to swap to * @param synthInAmount the amount of the synth to swap form * @return the expected amount of the bridging synth and the expected amount of the desired token */ function calcSynthToToken( ISwap swap, bytes32 synthInKey, uint8 tokenToIndex, uint256 synthInAmount ) external view returns (uint256, uint256) { IExchangeRates exchangeRates = IExchangeRates( SYNTHETIX_RESOLVER.getAddress(EXCHANGE_RATES_NAME) ); uint8 mediumSynthIndex = getSynthIndex(swap); bytes32 mediumSynthKey = getSynthKey(swap); require(synthInKey != mediumSynthKey, "use normal swap"); uint256 expectedMediumSynthAmount = exchangeRates.effectiveValue( synthInKey, synthInAmount, mediumSynthKey ); return ( expectedMediumSynthAmount, swap.calculateSwap( mediumSynthIndex, tokenToIndex, expectedMediumSynthAmount ) ); } /** * @notice Initiates a cross-asset swap from a synthetic asset to a supported token. The caller will receive * an ERC721 token representing their ownership of the pending cross-asset swap. * @param swap the address of a Saddle pool to use to swap the given token to a bridging synth * @param synthInKey the currency key of the synth to swap from * @param tokenToIndex the index of the token to swap to * @param synthInAmount the amount of the synth to swap form * @param minMediumSynthAmount the minimum amount of the bridging synth at pre-settlement stage * @return the ID of the ERC721 token sent to the caller */ function synthToToken( ISwap swap, bytes32 synthInKey, uint8 tokenToIndex, uint256 synthInAmount, uint256 minMediumSynthAmount ) external returns (uint256) { require(synthInAmount != 0, "amount must be greater than 0"); bytes32 mediumSynthKey = getSynthKey(swap); require( synthInKey != mediumSynthKey, "synth is supported via normal swap" ); // Create a SynthSwapper clone SynthSwapper synthSwapper = SynthSwapper( Clones.clone(SYNTH_SWAPPER_MASTER) ); synthSwapper.initialize(); // Add the synthswapper to the pending synth to token settlement list uint256 itemId = _addToPendingSynthToTokenSwapList( PendingToTokenSwap(synthSwapper, mediumSynthKey, swap, tokenToIndex) ); pendingSwapType[itemId] = PendingSwapType.SynthToToken; // Mint an ERC721 token that represents ownership of the pending synth to token settlement to msg.sender _mint(msg.sender, itemId); // Receive synth from the user and swap it to another synth IERC20 synthFrom = getProxyAddressFromTargetSynthKey(synthInKey); synthFrom.safeTransferFrom(msg.sender, address(this), synthInAmount); synthFrom.safeTransfer(address(synthSwapper), synthInAmount); require( synthSwapper.swapSynth(synthInKey, synthInAmount, mediumSynthKey) >= minMediumSynthAmount, "minMediumSynthAmount not reached" ); // Emit SynthToToken event with relevant data emit SynthToToken( msg.sender, itemId, swap, synthInKey, synthInAmount, tokenToIndex ); return (itemId); } /** * @notice Calculates the expected amount of the desired token the caller will receive after completing * a `TokenToToken` swap with the given parameters. This calculation does not consider the settlement periods or * any potential changes of the pool compositions. * @param swaps the addresses of the two Saddle pools used to do the cross-asset swap * @param tokenFromIndex the index of the token in the first `swaps` pool to swap from * @param tokenToIndex the index of the token in the second `swaps` pool to swap to * @param tokenFromAmount the amount of the token to swap from * @return the expected amount of bridging synth at pre-settlement stage and the expected amount of the desired * token */ function calcTokenToToken( ISwap[2] calldata swaps, uint8 tokenFromIndex, uint8 tokenToIndex, uint256 tokenFromAmount ) external view returns (uint256, uint256) { IExchangeRates exchangeRates = IExchangeRates( SYNTHETIX_RESOLVER.getAddress(EXCHANGE_RATES_NAME) ); uint256 firstSynthAmount = swaps[0].calculateSwap( tokenFromIndex, getSynthIndex(swaps[0]), tokenFromAmount ); uint256 mediumSynthAmount = exchangeRates.effectiveValue( getSynthKey(swaps[0]), firstSynthAmount, getSynthKey(swaps[1]) ); return ( mediumSynthAmount, swaps[1].calculateSwap( getSynthIndex(swaps[1]), tokenToIndex, mediumSynthAmount ) ); } /** * @notice Initiates a cross-asset swap from a token in one Saddle pool to one in another. The caller will receive * an ERC721 token representing their ownership of the pending cross-asset swap. * @param swaps the addresses of the two Saddle pools used to do the cross-asset swap * @param tokenFromIndex the index of the token in the first `swaps` pool to swap from * @param tokenToIndex the index of the token in the second `swaps` pool to swap to * @param tokenFromAmount the amount of the token to swap from * @param minMediumSynthAmount the minimum amount of the bridging synth at pre-settlement stage * @return the ID of the ERC721 token sent to the caller */ function tokenToToken( ISwap[2] calldata swaps, uint8 tokenFromIndex, uint8 tokenToIndex, uint256 tokenFromAmount, uint256 minMediumSynthAmount ) external returns (uint256) { // Create a SynthSwapper clone require(tokenFromAmount != 0, "amount must be greater than 0"); SynthSwapper synthSwapper = SynthSwapper( Clones.clone(SYNTH_SWAPPER_MASTER) ); synthSwapper.initialize(); bytes32 mediumSynthKey = getSynthKey(swaps[1]); // Add the synthswapper to the pending synth to token settlement list uint256 itemId = _addToPendingSynthToTokenSwapList( PendingToTokenSwap( synthSwapper, mediumSynthKey, swaps[1], tokenToIndex ) ); pendingSwapType[itemId] = PendingSwapType.TokenToToken; // Mint an ERC721 token that represents ownership of the pending swap to msg.sender _mint(msg.sender, itemId); // Receive token from the user ISwap swap = swaps[0]; { IERC20 tokenFrom = swapContracts[address(swap)].tokens[ tokenFromIndex ]; tokenFrom.safeTransferFrom( msg.sender, address(this), tokenFromAmount ); } uint256 firstSynthAmount = swap.swap( tokenFromIndex, getSynthIndex(swap), tokenFromAmount, 0, block.timestamp ); // Swap the synth to another synth IERC20(getSynthAddress(swap)).safeTransfer( address(synthSwapper), firstSynthAmount ); require( synthSwapper.swapSynth( getSynthKey(swap), firstSynthAmount, mediumSynthKey ) >= minMediumSynthAmount, "minMediumSynthAmount not reached" ); // Emit TokenToToken event with relevant data emit TokenToToken( msg.sender, itemId, swaps, tokenFromIndex, tokenFromAmount, tokenToIndex ); return (itemId); } /** * @notice Registers the index and the address of the supported synth from the given `swap` pool. The matching currency key must * be supplied for a successful registration. * @param swap the address of the pool that contains the synth * @param synthIndex the index of the supported synth in the given `swap` pool * @param currencyKey the currency key of the synth in bytes32 form */ function setSynthIndex( ISwap swap, uint8 synthIndex, bytes32 currencyKey ) external { require(synthIndex < MAX_UINT8, "index is too large"); SwapContractInfo storage swapContractInfo = swapContracts[ address(swap) ]; // Check if the pool has already been added require(swapContractInfo.synthIndexPlusOne == 0, "Pool already added"); // Ensure the synth with the same currency key exists at the given `synthIndex` IERC20 synth = swap.getToken(synthIndex); require( ISynth(Proxy(address(synth)).target()).currencyKey() == currencyKey, "currencyKey does not match" ); swapContractInfo.synthIndexPlusOne = synthIndex + 1; swapContractInfo.synthAddress = address(synth); swapContractInfo.synthKey = currencyKey; swapContractInfo.tokens = new IERC20[](0); for (uint8 i = 0; i < MAX_UINT8; i++) { IERC20 token; if (i == synthIndex) { token = synth; } else { try swap.getToken(i) returns (IERC20 token_) { token = token_; } catch { break; } } swapContractInfo.tokens.push(token); token.safeApprove(address(swap), MAX_UINT256); } emit SynthIndex(address(swap), synthIndex, currencyKey, address(synth)); } /** * @notice Returns the index of the supported synth in the given `swap` pool. Reverts if the `swap` pool * is not registered. * @param swap the address of the pool that contains the synth * @return the index of the supported synth */ function getSynthIndex(ISwap swap) public view returns (uint8) { uint8 synthIndexPlusOne = swapContracts[address(swap)] .synthIndexPlusOne; require(synthIndexPlusOne > 0, "synth index not found for given pool"); return synthIndexPlusOne - 1; } /** * @notice Returns the address of the supported synth in the given `swap` pool. Reverts if the `swap` pool * is not registered. * @param swap the address of the pool that contains the synth * @return the address of the supported synth */ function getSynthAddress(ISwap swap) public view returns (address) { address synthAddress = swapContracts[address(swap)].synthAddress; require( synthAddress != address(0), "synth addr not found for given pool" ); return synthAddress; } /** * @notice Returns the currency key of the supported synth in the given `swap` pool. Reverts if the `swap` pool * is not registered. * @param swap the address of the pool that contains the synth * @return the currency key of the supported synth */ function getSynthKey(ISwap swap) public view returns (bytes32) { bytes32 synthKey = swapContracts[address(swap)].synthKey; require(synthKey != 0x0, "synth key not found for given pool"); return synthKey; } /** * @notice Updates the stored address of the `EXCHANGER` contract. When the Synthetix team upgrades their protocol, * a new Exchanger contract is deployed. This function manually updates the stored address. */ function updateExchangerCache() public { exchanger = IExchanger(SYNTHETIX_RESOLVER.getAddress(EXCHANGER_NAME)); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../../utils/Context.sol"; import "./IERC721.sol"; import "./IERC721Metadata.sol"; import "./IERC721Enumerable.sol"; import "./IERC721Receiver.sol"; import "../../introspection/ERC165.sol"; import "../../math/SafeMath.sol"; import "../../utils/Address.sol"; import "../../utils/EnumerableSet.sol"; import "../../utils/EnumerableMap.sol"; import "../../utils/Strings.sol"; /** * @title ERC721 Non-Fungible Token Standard basic implementation * @dev see https://eips.ethereum.org/EIPS/eip-721 */ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable { using SafeMath for uint256; using Address for address; using EnumerableSet for EnumerableSet.UintSet; using EnumerableMap for EnumerableMap.UintToAddressMap; using Strings for uint256; // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector` bytes4 private constant _ERC721_RECEIVED = 0x150b7a02; // Mapping from holder address to their (enumerable) set of owned tokens mapping (address => EnumerableSet.UintSet) private _holderTokens; // Enumerable mapping from token ids to their owners EnumerableMap.UintToAddressMap private _tokenOwners; // Mapping from token ID to approved address mapping (uint256 => address) private _tokenApprovals; // Mapping from owner to operator approvals mapping (address => mapping (address => bool)) private _operatorApprovals; // Token name string private _name; // Token symbol string private _symbol; // Optional mapping for token URIs mapping (uint256 => string) private _tokenURIs; // Base URI string private _baseURI; /* * bytes4(keccak256('balanceOf(address)')) == 0x70a08231 * bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e * bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3 * bytes4(keccak256('getApproved(uint256)')) == 0x081812fc * bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465 * bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5 * bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd * bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e * bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde * * => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^ * 0xa22cb465 ^ 0xe985e9c5 ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd */ bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd; /* * bytes4(keccak256('name()')) == 0x06fdde03 * bytes4(keccak256('symbol()')) == 0x95d89b41 * bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd * * => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f */ bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f; /* * bytes4(keccak256('totalSupply()')) == 0x18160ddd * bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59 * bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7 * * => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63 */ bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63; /** * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. */ constructor (string memory name_, string memory symbol_) public { _name = name_; _symbol = symbol_; // register the supported interfaces to conform to ERC721 via ERC165 _registerInterface(_INTERFACE_ID_ERC721); _registerInterface(_INTERFACE_ID_ERC721_METADATA); _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE); } /** * @dev See {IERC721-balanceOf}. */ function balanceOf(address owner) public view virtual override returns (uint256) { require(owner != address(0), "ERC721: balance query for the zero address"); return _holderTokens[owner].length(); } /** * @dev See {IERC721-ownerOf}. */ function ownerOf(uint256 tokenId) public view virtual override returns (address) { return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token"); } /** * @dev See {IERC721Metadata-name}. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev See {IERC721Metadata-symbol}. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev See {IERC721Metadata-tokenURI}. */ function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); string memory _tokenURI = _tokenURIs[tokenId]; string memory base = baseURI(); // If there is no base URI, return the token URI. if (bytes(base).length == 0) { return _tokenURI; } // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked). if (bytes(_tokenURI).length > 0) { return string(abi.encodePacked(base, _tokenURI)); } // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI. return string(abi.encodePacked(base, tokenId.toString())); } /** * @dev Returns the base URI set via {_setBaseURI}. This will be * automatically added as a prefix in {tokenURI} to each token's URI, or * to the token ID if no specific URI is set for that token ID. */ function baseURI() public view virtual returns (string memory) { return _baseURI; } /** * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}. */ function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) { return _holderTokens[owner].at(index); } /** * @dev See {IERC721Enumerable-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { // _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds return _tokenOwners.length(); } /** * @dev See {IERC721Enumerable-tokenByIndex}. */ function tokenByIndex(uint256 index) public view virtual override returns (uint256) { (uint256 tokenId, ) = _tokenOwners.at(index); return tokenId; } /** * @dev See {IERC721-approve}. */ function approve(address to, uint256 tokenId) public virtual override { address owner = ERC721.ownerOf(tokenId); require(to != owner, "ERC721: approval to current owner"); require(_msgSender() == owner || ERC721.isApprovedForAll(owner, _msgSender()), "ERC721: approve caller is not owner nor approved for all" ); _approve(to, tokenId); } /** * @dev See {IERC721-getApproved}. */ function getApproved(uint256 tokenId) public view virtual override returns (address) { require(_exists(tokenId), "ERC721: approved query for nonexistent token"); return _tokenApprovals[tokenId]; } /** * @dev See {IERC721-setApprovalForAll}. */ function setApprovalForAll(address operator, bool approved) public virtual override { require(operator != _msgSender(), "ERC721: approve to caller"); _operatorApprovals[_msgSender()][operator] = approved; emit ApprovalForAll(_msgSender(), operator, approved); } /** * @dev See {IERC721-isApprovedForAll}. */ function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { return _operatorApprovals[owner][operator]; } /** * @dev See {IERC721-transferFrom}. */ function transferFrom(address from, address to, uint256 tokenId) public virtual override { //solhint-disable-next-line max-line-length require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); _transfer(from, to, tokenId); } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override { safeTransferFrom(from, to, tokenId, ""); } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override { require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); _safeTransfer(from, to, tokenId, _data); } /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * `_data` is additional data, it has no specified format and it is sent in call to `to`. * * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. * implement alternative mechanisms to perform token transfer, such as signature-based. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual { _transfer(from, to, tokenId); require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); } /** * @dev Returns whether `tokenId` exists. * * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. * * Tokens start existing when they are minted (`_mint`), * and stop existing when they are burned (`_burn`). */ function _exists(uint256 tokenId) internal view virtual returns (bool) { return _tokenOwners.contains(tokenId); } /** * @dev Returns whether `spender` is allowed to manage `tokenId`. * * Requirements: * * - `tokenId` must exist. */ function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { require(_exists(tokenId), "ERC721: operator query for nonexistent token"); address owner = ERC721.ownerOf(tokenId); return (spender == owner || getApproved(tokenId) == spender || ERC721.isApprovedForAll(owner, spender)); } /** * @dev Safely mints `tokenId` and transfers it to `to`. * * Requirements: d* * - `tokenId` must not exist. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeMint(address to, uint256 tokenId) internal virtual { _safeMint(to, tokenId, ""); } /** * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. */ function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual { _mint(to, tokenId); require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); } /** * @dev Mints `tokenId` and transfers it to `to`. * * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible * * Requirements: * * - `tokenId` must not exist. * - `to` cannot be the zero address. * * Emits a {Transfer} event. */ function _mint(address to, uint256 tokenId) internal virtual { require(to != address(0), "ERC721: mint to the zero address"); require(!_exists(tokenId), "ERC721: token already minted"); _beforeTokenTransfer(address(0), to, tokenId); _holderTokens[to].add(tokenId); _tokenOwners.set(tokenId, to); emit Transfer(address(0), to, tokenId); } /** * @dev Destroys `tokenId`. * The approval is cleared when the token is burned. * * Requirements: * * - `tokenId` must exist. * * Emits a {Transfer} event. */ function _burn(uint256 tokenId) internal virtual { address owner = ERC721.ownerOf(tokenId); // internal owner _beforeTokenTransfer(owner, address(0), tokenId); // Clear approvals _approve(address(0), tokenId); // Clear metadata (if any) if (bytes(_tokenURIs[tokenId]).length != 0) { delete _tokenURIs[tokenId]; } _holderTokens[owner].remove(tokenId); _tokenOwners.remove(tokenId); emit Transfer(owner, address(0), tokenId); } /** * @dev Transfers `tokenId` from `from` to `to`. * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. * * Requirements: * * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * * Emits a {Transfer} event. */ function _transfer(address from, address to, uint256 tokenId) internal virtual { require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); // internal owner require(to != address(0), "ERC721: transfer to the zero address"); _beforeTokenTransfer(from, to, tokenId); // Clear approvals from the previous owner _approve(address(0), tokenId); _holderTokens[from].remove(tokenId); _holderTokens[to].add(tokenId); _tokenOwners.set(tokenId, to); emit Transfer(from, to, tokenId); } /** * @dev Sets `_tokenURI` as the tokenURI of `tokenId`. * * Requirements: * * - `tokenId` must exist. */ function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token"); _tokenURIs[tokenId] = _tokenURI; } /** * @dev Internal function to set the base URI for all token IDs. It is * automatically added as a prefix to the value returned in {tokenURI}, * or to the token ID if {tokenURI} is empty. */ function _setBaseURI(string memory baseURI_) internal virtual { _baseURI = baseURI_; } /** * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. * The call is not executed if the target address is not a contract. * * @param from address representing the previous owner of the given token ID * @param to target address that will receive the tokens * @param tokenId uint256 ID of the token to be transferred * @param _data bytes optional data to send along with the call * @return bool whether the call correctly returned the expected magic value */ function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) private returns (bool) { if (!to.isContract()) { return true; } bytes memory returndata = to.functionCall(abi.encodeWithSelector( IERC721Receiver(to).onERC721Received.selector, _msgSender(), from, tokenId, _data ), "ERC721: transfer to non ERC721Receiver implementer"); bytes4 retval = abi.decode(returndata, (bytes4)); return (retval == _ERC721_RECEIVED); } /** * @dev Approve `to` to operate on `tokenId` * * Emits an {Approval} event. */ function _approve(address to, uint256 tokenId) internal virtual { _tokenApprovals[tokenId] = to; emit Approval(ERC721.ownerOf(tokenId), to, tokenId); // internal owner } /** * @dev Hook that is called before any token transfer. This includes minting * and burning. * * Calling conditions: * * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be * transferred to `to`. * - When `from` is zero, `tokenId` will be minted for `to`. * - When `to` is zero, ``from``'s `tokenId` will be burned. * - `from` cannot be the zero address. * - `to` cannot be the zero address. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { } }
pragma solidity >=0.4.24; // https://docs.synthetix.io/contracts/source/interfaces/iaddressresolver interface IAddressResolver { function getAddress(bytes32 name) external view returns (address); function getSynth(bytes32 key) external view returns (address); function requireAndGetAddress(bytes32 name, string calldata reason) external view returns (address); }
pragma solidity >=0.4.24; import "./IVirtualSynth.sol"; // https://docs.synthetix.io/contracts/source/interfaces/iexchanger interface IExchanger { // Views function calculateAmountAfterSettlement( address from, bytes32 currencyKey, uint amount, uint refunded ) external view returns (uint amountAfterSettlement); function isSynthRateInvalid(bytes32 currencyKey) external view returns (bool); function maxSecsLeftInWaitingPeriod(address account, bytes32 currencyKey) external view returns (uint); function settlementOwing(address account, bytes32 currencyKey) external view returns ( uint reclaimAmount, uint rebateAmount, uint numEntries ); function hasWaitingPeriodOrSettlementOwing(address account, bytes32 currencyKey) external view returns (bool); function feeRateForExchange(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view returns (uint exchangeFeeRate); function getAmountsForExchange( uint sourceAmount, bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey ) external view returns ( uint amountReceived, uint fee, uint exchangeFeeRate ); function priceDeviationThresholdFactor() external view returns (uint); function waitingPeriodSecs() external view returns (uint); // Mutative functions function exchange( address from, bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, address destinationAddress ) external returns (uint amountReceived); function exchangeOnBehalf( address exchangeForAddress, address from, bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey ) external returns (uint amountReceived); function exchangeWithTracking( address from, bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, address destinationAddress, address originator, bytes32 trackingCode ) external returns (uint amountReceived); function exchangeOnBehalfWithTracking( address exchangeForAddress, address from, bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, address originator, bytes32 trackingCode ) external returns (uint amountReceived); function exchangeWithVirtual( address from, bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, address destinationAddress, bytes32 trackingCode ) external returns (uint amountReceived, IVirtualSynth vSynth); function settle(address from, bytes32 currencyKey) external returns ( uint reclaimed, uint refunded, uint numEntries ); function setLastExchangeRateForSynth(bytes32 currencyKey, uint rate) external; function suspendSynthWithInvalidRate(bytes32 currencyKey) external; }
pragma solidity >=0.4.24; // https://docs.synthetix.io/contracts/source/interfaces/iexchangerates interface IExchangeRates { // Structs struct RateAndUpdatedTime { uint216 rate; uint40 time; } struct InversePricing { uint entryPoint; uint upperLimit; uint lowerLimit; bool frozenAtUpperLimit; bool frozenAtLowerLimit; } // Views function aggregators(bytes32 currencyKey) external view returns (address); function aggregatorWarningFlags() external view returns (address); function anyRateIsInvalid(bytes32[] calldata currencyKeys) external view returns (bool); function canFreezeRate(bytes32 currencyKey) external view returns (bool); function currentRoundForRate(bytes32 currencyKey) external view returns (uint); function currenciesUsingAggregator(address aggregator) external view returns (bytes32[] memory); function effectiveValue( bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey ) external view returns (uint value); function effectiveValueAndRates( bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey ) external view returns ( uint value, uint sourceRate, uint destinationRate ); function effectiveValueAtRound( bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, uint roundIdForSrc, uint roundIdForDest ) external view returns (uint value); function getCurrentRoundId(bytes32 currencyKey) external view returns (uint); function getLastRoundIdBeforeElapsedSecs( bytes32 currencyKey, uint startingRoundId, uint startingTimestamp, uint timediff ) external view returns (uint); function inversePricing(bytes32 currencyKey) external view returns ( uint entryPoint, uint upperLimit, uint lowerLimit, bool frozenAtUpperLimit, bool frozenAtLowerLimit ); function lastRateUpdateTimes(bytes32 currencyKey) external view returns (uint256); function oracle() external view returns (address); function rateAndTimestampAtRound(bytes32 currencyKey, uint roundId) external view returns (uint rate, uint time); function rateAndUpdatedTime(bytes32 currencyKey) external view returns (uint rate, uint time); function rateAndInvalid(bytes32 currencyKey) external view returns (uint rate, bool isInvalid); function rateForCurrency(bytes32 currencyKey) external view returns (uint); function rateIsFlagged(bytes32 currencyKey) external view returns (bool); function rateIsFrozen(bytes32 currencyKey) external view returns (bool); function rateIsInvalid(bytes32 currencyKey) external view returns (bool); function rateIsStale(bytes32 currencyKey) external view returns (bool); function rateStalePeriod() external view returns (uint); function ratesAndUpdatedTimeForCurrencyLastNRounds(bytes32 currencyKey, uint numRounds) external view returns (uint[] memory rates, uint[] memory times); function ratesAndInvalidForCurrencies(bytes32[] calldata currencyKeys) external view returns (uint[] memory rates, bool anyRateInvalid); function ratesForCurrencies(bytes32[] calldata currencyKeys) external view returns (uint[] memory); // Mutative functions function freezeRate(bytes32 currencyKey) external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; import "../../introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 tokenId) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; import "./IERC721.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721Metadata is IERC721 { /** * @dev Returns the token collection name. */ function name() external view returns (string memory); /** * @dev Returns the token collection symbol. */ function symbol() external view returns (string memory); /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256 tokenId) external view returns (string memory); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; import "./IERC721.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721Enumerable is IERC721 { /** * @dev Returns the total amount of tokens stored by the contract. */ function totalSupply() external view returns (uint256); /** * @dev Returns a token ID owned by `owner` at a given `index` of its token list. * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. */ function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId); /** * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. * Use along with {totalSupply} to enumerate all tokens. */ function tokenByIndex(uint256 index) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. * * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`. */ function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts may inherit from this and call {_registerInterface} to declare * their support of an interface. */ abstract contract ERC165 is IERC165 { /* * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7 */ bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7; /** * @dev Mapping of interface ids to whether or not it's supported. */ mapping(bytes4 => bool) private _supportedInterfaces; constructor () internal { // Derived contracts need only register support for their own interfaces, // we register support for ERC165 itself here _registerInterface(_INTERFACE_ID_ERC165); } /** * @dev See {IERC165-supportsInterface}. * * Time complexity O(1), guaranteed to always use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return _supportedInterfaces[interfaceId]; } /** * @dev Registers the contract as an implementer of the interface defined by * `interfaceId`. Support of the actual ERC165 interface is automatic and * registering its interface id is not required. * * See {IERC165-supportsInterface}. * * Requirements: * * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`). */ function _registerInterface(bytes4 interfaceId) internal virtual { require(interfaceId != 0xffffffff, "ERC165: invalid interface id"); _supportedInterfaces[interfaceId] = true; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping (bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement. bytes32 lastvalue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastvalue; // Update the index for the moved value set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { require(set._values.length > index, "EnumerableSet: index out of bounds"); return set._values[index]; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values on the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Library for managing an enumerable variant of Solidity's * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] * type. * * Maps have the following properties: * * - Entries are added, removed, and checked for existence in constant time * (O(1)). * - Entries are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableMap for EnumerableMap.UintToAddressMap; * * // Declare a set state variable * EnumerableMap.UintToAddressMap private myMap; * } * ``` * * As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are * supported. */ library EnumerableMap { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Map type with // bytes32 keys and values. // The Map implementation uses private functions, and user-facing // implementations (such as Uint256ToAddressMap) are just wrappers around // the underlying Map. // This means that we can only create new EnumerableMaps for types that fit // in bytes32. struct MapEntry { bytes32 _key; bytes32 _value; } struct Map { // Storage of map keys and values MapEntry[] _entries; // Position of the entry defined by a key in the `entries` array, plus 1 // because index 0 means a key is not in the map. mapping (bytes32 => uint256) _indexes; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) { // We read and store the key's index to prevent multiple reads from the same storage slot uint256 keyIndex = map._indexes[key]; if (keyIndex == 0) { // Equivalent to !contains(map, key) map._entries.push(MapEntry({ _key: key, _value: value })); // The entry is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value map._indexes[key] = map._entries.length; return true; } else { map._entries[keyIndex - 1]._value = value; return false; } } /** * @dev Removes a key-value pair from a map. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function _remove(Map storage map, bytes32 key) private returns (bool) { // We read and store the key's index to prevent multiple reads from the same storage slot uint256 keyIndex = map._indexes[key]; if (keyIndex != 0) { // Equivalent to contains(map, key) // To delete a key-value pair from the _entries array in O(1), we swap the entry to delete with the last one // in the array, and then remove the last entry (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = keyIndex - 1; uint256 lastIndex = map._entries.length - 1; // When the entry to delete is the last one, the swap operation is unnecessary. However, since this occurs // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement. MapEntry storage lastEntry = map._entries[lastIndex]; // Move the last entry to the index where the entry to delete is map._entries[toDeleteIndex] = lastEntry; // Update the index for the moved entry map._indexes[lastEntry._key] = toDeleteIndex + 1; // All indexes are 1-based // Delete the slot where the moved entry was stored map._entries.pop(); // Delete the index for the deleted slot delete map._indexes[key]; return true; } else { return false; } } /** * @dev Returns true if the key is in the map. O(1). */ function _contains(Map storage map, bytes32 key) private view returns (bool) { return map._indexes[key] != 0; } /** * @dev Returns the number of key-value pairs in the map. O(1). */ function _length(Map storage map) private view returns (uint256) { return map._entries.length; } /** * @dev Returns the key-value pair stored at position `index` in the map. O(1). * * Note that there are no guarantees on the ordering of entries inside the * array, and it may change when more entries are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) { require(map._entries.length > index, "EnumerableMap: index out of bounds"); MapEntry storage entry = map._entries[index]; return (entry._key, entry._value); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. */ function _tryGet(Map storage map, bytes32 key) private view returns (bool, bytes32) { uint256 keyIndex = map._indexes[key]; if (keyIndex == 0) return (false, 0); // Equivalent to contains(map, key) return (true, map._entries[keyIndex - 1]._value); // All indexes are 1-based } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function _get(Map storage map, bytes32 key) private view returns (bytes32) { uint256 keyIndex = map._indexes[key]; require(keyIndex != 0, "EnumerableMap: nonexistent key"); // Equivalent to contains(map, key) return map._entries[keyIndex - 1]._value; // All indexes are 1-based } /** * @dev Same as {_get}, with a custom error message when `key` is not in the map. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {_tryGet}. */ function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) { uint256 keyIndex = map._indexes[key]; require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key) return map._entries[keyIndex - 1]._value; // All indexes are 1-based } // UintToAddressMap struct UintToAddressMap { Map _inner; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) { return _set(map._inner, bytes32(key), bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) { return _remove(map._inner, bytes32(key)); } /** * @dev Returns true if the key is in the map. O(1). */ function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) { return _contains(map._inner, bytes32(key)); } /** * @dev Returns the number of elements in the map. O(1). */ function length(UintToAddressMap storage map) internal view returns (uint256) { return _length(map._inner); } /** * @dev Returns the element stored at position `index` in the set. O(1). * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) { (bytes32 key, bytes32 value) = _at(map._inner, index); return (uint256(key), address(uint160(uint256(value)))); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. * * _Available since v3.4._ */ function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) { (bool success, bytes32 value) = _tryGet(map._inner, bytes32(key)); return (success, address(uint160(uint256(value)))); } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function get(UintToAddressMap storage map, uint256 key) internal view returns (address) { return address(uint160(uint256(_get(map._inner, bytes32(key))))); } /** * @dev Same as {get}, with a custom error message when `key` is not in the map. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryGet}. */ function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) { return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage)))); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev String operations. */ library Strings { /** * @dev Converts a `uint256` to its ASCII `string` representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); uint256 index = digits - 1; temp = value; while (temp != 0) { buffer[index--] = bytes1(uint8(48 + temp % 10)); temp /= 10; } return string(buffer); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "./interfaces/ISwap.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; /** * @title SwapMigrator * @notice This contract is responsible for migrating old USD pool liquidity to the new ones. * Users can use this contract to remove their liquidity from the old pools and add them to the new * ones with a single transaction. */ contract SwapMigrator { using SafeERC20 for IERC20; struct MigrationData { address oldPoolAddress; IERC20 oldPoolLPTokenAddress; address newPoolAddress; IERC20 newPoolLPTokenAddress; IERC20[] underlyingTokens; } MigrationData public usdPoolMigrationData; address public owner; uint256 private constant MAX_UINT256 = 2**256 - 1; /** * @notice Sets the storage variables and approves tokens to be used by the old and new swap contracts * @param usdData_ MigrationData struct with information about old and new USD pools * @param owner_ owner that is allowed to call the `rescue()` function */ constructor(MigrationData memory usdData_, address owner_) public { // Approve old USD LP Token to be used by the old USD pool usdData_.oldPoolLPTokenAddress.approve( usdData_.oldPoolAddress, MAX_UINT256 ); // Approve USD tokens to be used by the new USD pool for (uint256 i = 0; i < usdData_.underlyingTokens.length; i++) { usdData_.underlyingTokens[i].safeApprove( usdData_.newPoolAddress, MAX_UINT256 ); } // Set storage variables usdPoolMigrationData = usdData_; owner = owner_; } /** * @notice Migrates old USD pool's LPToken to the new pool * @param amount Amount of old LPToken to migrate * @param minAmount Minimum amount of new LPToken to receive */ function migrateUSDPool(uint256 amount, uint256 minAmount) external returns (uint256) { // Transfer old LP token from the caller usdPoolMigrationData.oldPoolLPTokenAddress.safeTransferFrom( msg.sender, address(this), amount ); // Remove liquidity from the old pool and add them to the new pool uint256[] memory amounts = ISwap(usdPoolMigrationData.oldPoolAddress) .removeLiquidity( amount, new uint256[](usdPoolMigrationData.underlyingTokens.length), MAX_UINT256 ); uint256 mintedAmount = ISwap(usdPoolMigrationData.newPoolAddress) .addLiquidity(amounts, minAmount, MAX_UINT256); // Transfer new LP Token to the caller usdPoolMigrationData.newPoolLPTokenAddress.safeTransfer( msg.sender, mintedAmount ); return mintedAmount; } /** * @notice Rescues any token that may be sent to this contract accidentally. * @param token Amount of old LPToken to migrate * @param to Minimum amount of new LPToken to receive */ function rescue(IERC20 token, address to) external { require(msg.sender == owner, "is not owner"); token.safeTransfer(to, token.balanceOf(address(this))); } }
// SPDX-License-Identifier: MIT // Generalized and adapted from https://github.com/k06a/Unipool 🙇 pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; /** * @title StakeableTokenWrapper * @notice A wrapper for an ERC-20 that can be staked and withdrawn. * @dev In this contract, staked tokens don't do anything- instead other * contracts can inherit from this one to add functionality. */ contract StakeableTokenWrapper { using SafeERC20 for IERC20; using SafeMath for uint256; uint256 public totalSupply; IERC20 public stakedToken; mapping(address => uint256) private _balances; event Staked(address indexed user, uint256 amount); event Withdrawn(address indexed user, uint256 amount); /** * @notice Creates a new StakeableTokenWrapper with given `_stakedToken` address * @param _stakedToken address of a token that will be used to stake */ constructor(IERC20 _stakedToken) public { stakedToken = _stakedToken; } /** * @notice Read how much `account` has staked in this contract * @param account address of an account * @return amount of total staked ERC20(this.stakedToken) by `account` */ function balanceOf(address account) external view returns (uint256) { return _balances[account]; } /** * @notice Stakes given `amount` in this contract * @param amount amount of ERC20(this.stakedToken) to stake */ function stake(uint256 amount) external { require(amount != 0, "amount == 0"); totalSupply = totalSupply.add(amount); _balances[msg.sender] = _balances[msg.sender].add(amount); stakedToken.safeTransferFrom(msg.sender, address(this), amount); emit Staked(msg.sender, amount); } /** * @notice Withdraws given `amount` from this contract * @param amount amount of ERC20(this.stakedToken) to withdraw */ function withdraw(uint256 amount) external { totalSupply = totalSupply.sub(amount); _balances[msg.sender] = _balances[msg.sender].sub(amount); stakedToken.safeTransfer(msg.sender, amount); emit Withdrawn(msg.sender, amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../interfaces/IFlashLoanReceiver.sol"; import "../interfaces/ISwapFlashLoan.sol"; import "hardhat/console.sol"; contract FlashLoanBorrowerExample is IFlashLoanReceiver { using SafeMath for uint256; // Typical executeOperation function should do the 3 following actions // 1. Check if the flashLoan was successful // 2. Do actions with the borrowed tokens // 3. Repay the debt to the `pool` function executeOperation( address pool, address token, uint256 amount, uint256 fee, bytes calldata params ) external override { // 1. Check if the flashLoan was valid require( IERC20(token).balanceOf(address(this)) >= amount, "flashloan is broken?" ); // 2. Do actions with the borrowed token bytes32 paramsHash = keccak256(params); if (paramsHash == keccak256(bytes("dontRepayDebt"))) { return; } else if (paramsHash == keccak256(bytes("reentrancy_addLiquidity"))) { ISwapFlashLoan(pool).addLiquidity( new uint256[](0), 0, block.timestamp ); } else if (paramsHash == keccak256(bytes("reentrancy_swap"))) { ISwapFlashLoan(pool).swap(1, 0, 1e6, 0, now); } else if ( paramsHash == keccak256(bytes("reentrancy_removeLiquidity")) ) { ISwapFlashLoan(pool).removeLiquidity(1e18, new uint256[](0), now); } else if ( paramsHash == keccak256(bytes("reentrancy_removeLiquidityOneToken")) ) { ISwapFlashLoan(pool).removeLiquidityOneToken(1e18, 0, 1e18, now); } // 3. Payback debt uint256 totalDebt = amount.add(fee); IERC20(token).transfer(pool, totalDebt); } function flashLoan( ISwapFlashLoan swap, IERC20 token, uint256 amount, bytes memory params ) external { swap.flashLoan(address(this), token, amount, params); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "./ISwap.sol"; interface ISwapFlashLoan is ISwap { function flashLoan( address receiver, IERC20 token, uint256 amount, bytes memory params ) external; }
// SPDX-License-Identifier: MIT pragma solidity >= 0.4.22 <0.9.0; library console { address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); function _sendLogPayload(bytes memory payload) private view { uint256 payloadLength = payload.length; address consoleAddress = CONSOLE_ADDRESS; assembly { let payloadStart := add(payload, 32) let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) } } function log() internal view { _sendLogPayload(abi.encodeWithSignature("log()")); } function logInt(int p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(int)", p0)); } function logUint(uint p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); } function logString(string memory p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); } function logBool(bool p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); } function logAddress(address p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); } function logBytes(bytes memory p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); } function logBytes1(bytes1 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); } function logBytes2(bytes2 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); } function logBytes3(bytes3 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); } function logBytes4(bytes4 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); } function logBytes5(bytes5 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); } function logBytes6(bytes6 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); } function logBytes7(bytes7 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); } function logBytes8(bytes8 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); } function logBytes9(bytes9 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); } function logBytes10(bytes10 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); } function logBytes11(bytes11 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); } function logBytes12(bytes12 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); } function logBytes13(bytes13 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); } function logBytes14(bytes14 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); } function logBytes15(bytes15 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); } function logBytes16(bytes16 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); } function logBytes17(bytes17 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); } function logBytes18(bytes18 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); } function logBytes19(bytes19 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); } function logBytes20(bytes20 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); } function logBytes21(bytes21 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); } function logBytes22(bytes22 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); } function logBytes23(bytes23 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); } function logBytes24(bytes24 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); } function logBytes25(bytes25 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); } function logBytes26(bytes26 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); } function logBytes27(bytes27 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); } function logBytes28(bytes28 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); } function logBytes29(bytes29 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); } function logBytes30(bytes30 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); } function logBytes31(bytes31 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); } function logBytes32(bytes32 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); } function log(uint p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); } function log(string memory p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); } function log(bool p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); } function log(address p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); } function log(uint p0, uint p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1)); } function log(uint p0, string memory p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1)); } function log(uint p0, bool p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1)); } function log(uint p0, address p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1)); } function log(string memory p0, uint p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1)); } function log(string memory p0, string memory p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); } function log(string memory p0, bool p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); } function log(string memory p0, address p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); } function log(bool p0, uint p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1)); } function log(bool p0, string memory p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); } function log(bool p0, bool p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); } function log(bool p0, address p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); } function log(address p0, uint p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1)); } function log(address p0, string memory p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); } function log(address p0, bool p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); } function log(address p0, address p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); } function log(uint p0, uint p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2)); } function log(uint p0, uint p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2)); } function log(uint p0, uint p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2)); } function log(uint p0, uint p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2)); } function log(uint p0, string memory p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2)); } function log(uint p0, string memory p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2)); } function log(uint p0, string memory p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2)); } function log(uint p0, string memory p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2)); } function log(uint p0, bool p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2)); } function log(uint p0, bool p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2)); } function log(uint p0, bool p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2)); } function log(uint p0, bool p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2)); } function log(uint p0, address p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2)); } function log(uint p0, address p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2)); } function log(uint p0, address p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2)); } function log(uint p0, address p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2)); } function log(string memory p0, uint p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2)); } function log(string memory p0, uint p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2)); } function log(string memory p0, uint p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2)); } function log(string memory p0, uint p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2)); } function log(string memory p0, string memory p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2)); } function log(string memory p0, string memory p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); } function log(string memory p0, string memory p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); } function log(string memory p0, string memory p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); } function log(string memory p0, bool p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2)); } function log(string memory p0, bool p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); } function log(string memory p0, bool p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); } function log(string memory p0, bool p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); } function log(string memory p0, address p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2)); } function log(string memory p0, address p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); } function log(string memory p0, address p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); } function log(string memory p0, address p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); } function log(bool p0, uint p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2)); } function log(bool p0, uint p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2)); } function log(bool p0, uint p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2)); } function log(bool p0, uint p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2)); } function log(bool p0, string memory p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2)); } function log(bool p0, string memory p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); } function log(bool p0, string memory p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); } function log(bool p0, string memory p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); } function log(bool p0, bool p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2)); } function log(bool p0, bool p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); } function log(bool p0, bool p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); } function log(bool p0, bool p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); } function log(bool p0, address p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2)); } function log(bool p0, address p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); } function log(bool p0, address p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); } function log(bool p0, address p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); } function log(address p0, uint p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2)); } function log(address p0, uint p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2)); } function log(address p0, uint p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2)); } function log(address p0, uint p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2)); } function log(address p0, string memory p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2)); } function log(address p0, string memory p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); } function log(address p0, string memory p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); } function log(address p0, string memory p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); } function log(address p0, bool p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2)); } function log(address p0, bool p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); } function log(address p0, bool p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); } function log(address p0, bool p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); } function log(address p0, address p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2)); } function log(address p0, address p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); } function log(address p0, address p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); } function log(address p0, address p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); } function log(uint p0, uint p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3)); } function log(uint p0, uint p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3)); } function log(uint p0, uint p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3)); } function log(uint p0, uint p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3)); } function log(uint p0, uint p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3)); } function log(uint p0, uint p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3)); } function log(uint p0, uint p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3)); } function log(uint p0, uint p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3)); } function log(uint p0, uint p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3)); } function log(uint p0, uint p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3)); } function log(uint p0, uint p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3)); } function log(uint p0, uint p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3)); } function log(uint p0, uint p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3)); } function log(uint p0, uint p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3)); } function log(uint p0, uint p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3)); } function log(uint p0, uint p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3)); } function log(uint p0, bool p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3)); } function log(uint p0, bool p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3)); } function log(uint p0, bool p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3)); } function log(uint p0, bool p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3)); } function log(uint p0, bool p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3)); } function log(uint p0, bool p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3)); } function log(uint p0, bool p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3)); } function log(uint p0, bool p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3)); } function log(uint p0, bool p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3)); } function log(uint p0, bool p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3)); } function log(uint p0, bool p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3)); } function log(uint p0, bool p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3)); } function log(uint p0, bool p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3)); } function log(uint p0, bool p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3)); } function log(uint p0, bool p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3)); } function log(uint p0, bool p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3)); } function log(uint p0, address p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3)); } function log(uint p0, address p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3)); } function log(uint p0, address p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3)); } function log(uint p0, address p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3)); } function log(uint p0, address p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3)); } function log(uint p0, address p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3)); } function log(uint p0, address p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3)); } function log(uint p0, address p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3)); } function log(uint p0, address p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3)); } function log(uint p0, address p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3)); } function log(uint p0, address p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3)); } function log(uint p0, address p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3)); } function log(uint p0, address p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3)); } function log(uint p0, address p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3)); } function log(uint p0, address p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3)); } function log(uint p0, address p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); } function log(bool p0, uint p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3)); } function log(bool p0, uint p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3)); } function log(bool p0, uint p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3)); } function log(bool p0, uint p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3)); } function log(bool p0, uint p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3)); } function log(bool p0, uint p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3)); } function log(bool p0, uint p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3)); } function log(bool p0, uint p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3)); } function log(bool p0, uint p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3)); } function log(bool p0, uint p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3)); } function log(bool p0, uint p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, uint p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3)); } function log(bool p0, uint p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3)); } function log(bool p0, uint p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3)); } function log(bool p0, uint p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3)); } function log(bool p0, uint p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); } function log(address p0, uint p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3)); } function log(address p0, uint p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3)); } function log(address p0, uint p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3)); } function log(address p0, uint p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3)); } function log(address p0, uint p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3)); } function log(address p0, uint p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3)); } function log(address p0, uint p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3)); } function log(address p0, uint p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3)); } function log(address p0, uint p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3)); } function log(address p0, uint p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3)); } function log(address p0, uint p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3)); } function log(address p0, uint p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3)); } function log(address p0, uint p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3)); } function log(address p0, uint p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3)); } function log(address p0, uint p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3)); } function log(address p0, uint p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); } function log(address p0, address p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3)); } function log(address p0, address p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3)); } function log(address p0, address p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../../interfaces/ISwap.sol"; import "hardhat/console.sol"; contract TestSwapReturnValues { using SafeMath for uint256; ISwap public swap; IERC20 public lpToken; uint8 public n; uint256 public constant MAX_INT = 2**256 - 1; constructor( ISwap swapContract, IERC20 lpTokenContract, uint8 numOfTokens ) public { swap = swapContract; lpToken = lpTokenContract; n = numOfTokens; // Pre-approve tokens for (uint8 i; i < n; i++) { swap.getToken(i).approve(address(swap), MAX_INT); } lpToken.approve(address(swap), MAX_INT); } function test_swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy ) public { uint256 balanceBefore = swap.getToken(tokenIndexTo).balanceOf( address(this) ); uint256 returnValue = swap.swap( tokenIndexFrom, tokenIndexTo, dx, minDy, block.timestamp ); uint256 balanceAfter = swap.getToken(tokenIndexTo).balanceOf( address(this) ); console.log( "swap: Expected %s, got %s", balanceAfter.sub(balanceBefore), returnValue ); require( returnValue == balanceAfter.sub(balanceBefore), "swap()'s return value does not match received amount" ); } function test_addLiquidity(uint256[] calldata amounts, uint256 minToMint) public { uint256 balanceBefore = lpToken.balanceOf(address(this)); uint256 returnValue = swap.addLiquidity(amounts, minToMint, MAX_INT); uint256 balanceAfter = lpToken.balanceOf(address(this)); console.log( "addLiquidity: Expected %s, got %s", balanceAfter.sub(balanceBefore), returnValue ); require( returnValue == balanceAfter.sub(balanceBefore), "addLiquidity()'s return value does not match minted amount" ); } function test_removeLiquidity(uint256 amount, uint256[] memory minAmounts) public { uint256[] memory balanceBefore = new uint256[](n); uint256[] memory balanceAfter = new uint256[](n); for (uint8 i = 0; i < n; i++) { balanceBefore[i] = swap.getToken(i).balanceOf(address(this)); } uint256[] memory returnValue = swap.removeLiquidity( amount, minAmounts, MAX_INT ); for (uint8 i = 0; i < n; i++) { balanceAfter[i] = swap.getToken(i).balanceOf(address(this)); console.log( "removeLiquidity: Expected %s, got %s", balanceAfter[i].sub(balanceBefore[i]), returnValue[i] ); require( balanceAfter[i].sub(balanceBefore[i]) == returnValue[i], "removeLiquidity()'s return value does not match received amounts of tokens" ); } } function test_removeLiquidityImbalance( uint256[] calldata amounts, uint256 maxBurnAmount ) public { uint256 balanceBefore = lpToken.balanceOf(address(this)); uint256 returnValue = swap.removeLiquidityImbalance( amounts, maxBurnAmount, MAX_INT ); uint256 balanceAfter = lpToken.balanceOf(address(this)); console.log( "removeLiquidityImbalance: Expected %s, got %s", balanceBefore.sub(balanceAfter), returnValue ); require( returnValue == balanceBefore.sub(balanceAfter), "removeLiquidityImbalance()'s return value does not match burned lpToken amount" ); } function test_removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount ) public { uint256 balanceBefore = swap.getToken(tokenIndex).balanceOf( address(this) ); uint256 returnValue = swap.removeLiquidityOneToken( tokenAmount, tokenIndex, minAmount, MAX_INT ); uint256 balanceAfter = swap.getToken(tokenIndex).balanceOf( address(this) ); console.log( "removeLiquidityOneToken: Expected %s, got %s", balanceAfter.sub(balanceBefore), returnValue ); require( returnValue == balanceAfter.sub(balanceBefore), "removeLiquidityOneToken()'s return value does not match received token amount" ); } }
// SPDX-License-Identifier: MIT // https://etherscan.io/address/0x2b7a5a5923eca5c00c6572cf3e8e08384f563f93#code pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "./LPTokenGuarded.sol"; import "../MathUtils.sol"; /** * @title SwapUtils library * @notice A library to be used within Swap.sol. Contains functions responsible for custody and AMM functionalities. * @dev Contracts relying on this library must initialize SwapUtils.Swap struct then use this library * for SwapUtils.Swap struct. Note that this library contains both functions called by users and admins. * Admin functions should be protected within contracts using this library. */ library SwapUtilsGuarded { using SafeERC20 for IERC20; using SafeMath for uint256; using MathUtils for uint256; /*** EVENTS ***/ event TokenSwap( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); event AddLiquidity( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event RemoveLiquidity( address indexed provider, uint256[] tokenAmounts, uint256 lpTokenSupply ); event RemoveLiquidityOne( address indexed provider, uint256 lpTokenAmount, uint256 lpTokenSupply, uint256 boughtId, uint256 tokensBought ); event RemoveLiquidityImbalance( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event NewAdminFee(uint256 newAdminFee); event NewSwapFee(uint256 newSwapFee); event NewWithdrawFee(uint256 newWithdrawFee); event RampA( uint256 oldA, uint256 newA, uint256 initialTime, uint256 futureTime ); event StopRampA(uint256 currentA, uint256 time); struct Swap { // variables around the ramp management of A, // the amplification coefficient * n * (n - 1) // see https://www.curve.fi/stableswap-paper.pdf for details uint256 initialA; uint256 futureA; uint256 initialATime; uint256 futureATime; // fee calculation uint256 swapFee; uint256 adminFee; uint256 defaultWithdrawFee; LPTokenGuarded lpToken; // contract references for all tokens being pooled IERC20[] pooledTokens; // multipliers for each pooled token's precision to get to POOL_PRECISION_DECIMALS // for example, TBTC has 18 decimals, so the multiplier should be 1. WBTC // has 8, so the multiplier should be 10 ** 18 / 10 ** 8 => 10 ** 10 uint256[] tokenPrecisionMultipliers; // the pool balance of each token, in the token's precision // the contract's actual token balance might differ uint256[] balances; mapping(address => uint256) depositTimestamp; mapping(address => uint256) withdrawFeeMultiplier; } // Struct storing variables used in calculations in the // calculateWithdrawOneTokenDY function to avoid stack too deep errors struct CalculateWithdrawOneTokenDYInfo { uint256 d0; uint256 d1; uint256 newY; uint256 feePerToken; uint256 preciseA; } // Struct storing variables used in calculation in addLiquidity function // to avoid stack too deep error struct AddLiquidityInfo { uint256 d0; uint256 d1; uint256 d2; uint256 preciseA; } // Struct storing variables used in calculation in removeLiquidityImbalance function // to avoid stack too deep error struct RemoveLiquidityImbalanceInfo { uint256 d0; uint256 d1; uint256 d2; uint256 preciseA; } // the precision all pools tokens will be converted to uint8 public constant POOL_PRECISION_DECIMALS = 18; // the denominator used to calculate admin and LP fees. For example, an // LP fee might be something like tradeAmount.mul(fee).div(FEE_DENOMINATOR) uint256 private constant FEE_DENOMINATOR = 10**10; // Max swap fee is 1% or 100bps of each swap uint256 public constant MAX_SWAP_FEE = 10**8; // Max adminFee is 100% of the swapFee // adminFee does not add additional fee on top of swapFee // Instead it takes a certain % of the swapFee. Therefore it has no impact on the // users but only on the earnings of LPs uint256 public constant MAX_ADMIN_FEE = 10**10; // Max withdrawFee is 1% of the value withdrawn // Fee will be redistributed to the LPs in the pool, rewarding // long term providers. uint256 public constant MAX_WITHDRAW_FEE = 10**8; // Constant value used as max loop limit uint256 private constant MAX_LOOP_LIMIT = 256; // Constant values used in ramping A calculations uint256 public constant A_PRECISION = 100; uint256 public constant MAX_A = 10**6; uint256 private constant MAX_A_CHANGE = 2; uint256 private constant MIN_RAMP_TIME = 14 days; /*** VIEW & PURE FUNCTIONS ***/ /** * @notice Return A, the amplification coefficient * n * (n - 1) * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter */ function getA(Swap storage self) external view returns (uint256) { return _getA(self); } /** * @notice Return A, the amplification coefficient * n * (n - 1) * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter */ function _getA(Swap storage self) internal view returns (uint256) { return _getAPrecise(self).div(A_PRECISION); } /** * @notice Return A in its raw precision * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter in its raw precision form */ function getAPrecise(Swap storage self) external view returns (uint256) { return _getAPrecise(self); } /** * @notice Calculates and returns A based on the ramp settings * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter in its raw precision form */ function _getAPrecise(Swap storage self) internal view returns (uint256) { uint256 t1 = self.futureATime; // time when ramp is finished uint256 a1 = self.futureA; // final A value when ramp is finished if (block.timestamp < t1) { uint256 t0 = self.initialATime; // time when ramp is started uint256 a0 = self.initialA; // initial A value when ramp is started if (a1 > a0) { // a0 + (a1 - a0) * (block.timestamp - t0) / (t1 - t0) return a0.add( a1.sub(a0).mul(block.timestamp.sub(t0)).div(t1.sub(t0)) ); } else { // a0 - (a0 - a1) * (block.timestamp - t0) / (t1 - t0) return a0.sub( a0.sub(a1).mul(block.timestamp.sub(t0)).div(t1.sub(t0)) ); } } else { return a1; } } /** * @notice Retrieves the timestamp of last deposit made by the given address * @param self Swap struct to read from * @return timestamp of last deposit */ function getDepositTimestamp(Swap storage self, address user) external view returns (uint256) { return self.depositTimestamp[user]; } /** * @notice Calculate the dy, the amount of selected token that user receives and * the fee of withdrawing in one token * @param account the address that is withdrawing * @param tokenAmount the amount to withdraw in the pool's precision * @param tokenIndex which token will be withdrawn * @param self Swap struct to read from * @return the amount of token user will receive and the associated swap fee */ function calculateWithdrawOneToken( Swap storage self, address account, uint256 tokenAmount, uint8 tokenIndex ) public view returns (uint256, uint256) { uint256 dy; uint256 newY; (dy, newY) = calculateWithdrawOneTokenDY(self, tokenIndex, tokenAmount); // dy_0 (without fees) // dy, dy_0 - dy uint256 dySwapFee = _xp(self)[tokenIndex] .sub(newY) .div(self.tokenPrecisionMultipliers[tokenIndex]) .sub(dy); dy = dy .mul( FEE_DENOMINATOR.sub(calculateCurrentWithdrawFee(self, account)) ) .div(FEE_DENOMINATOR); return (dy, dySwapFee); } /** * @notice Calculate the dy of withdrawing in one token * @param self Swap struct to read from * @param tokenIndex which token will be withdrawn * @param tokenAmount the amount to withdraw in the pools precision * @return the d and the new y after withdrawing one token */ function calculateWithdrawOneTokenDY( Swap storage self, uint8 tokenIndex, uint256 tokenAmount ) internal view returns (uint256, uint256) { require( tokenIndex < self.pooledTokens.length, "Token index out of range" ); // Get the current D, then solve the stableswap invariant // y_i for D - tokenAmount uint256[] memory xp = _xp(self); CalculateWithdrawOneTokenDYInfo memory v = CalculateWithdrawOneTokenDYInfo(0, 0, 0, 0, 0); v.preciseA = _getAPrecise(self); v.d0 = getD(xp, v.preciseA); v.d1 = v.d0.sub(tokenAmount.mul(v.d0).div(self.lpToken.totalSupply())); require(tokenAmount <= xp[tokenIndex], "Withdraw exceeds available"); v.newY = getYD(v.preciseA, tokenIndex, xp, v.d1); uint256[] memory xpReduced = new uint256[](xp.length); v.feePerToken = _feePerToken(self); for (uint256 i = 0; i < self.pooledTokens.length; i++) { uint256 xpi = xp[i]; // if i == tokenIndex, dxExpected = xp[i] * d1 / d0 - newY // else dxExpected = xp[i] - (xp[i] * d1 / d0) // xpReduced[i] -= dxExpected * fee / FEE_DENOMINATOR xpReduced[i] = xpi.sub( ( (i == tokenIndex) ? xpi.mul(v.d1).div(v.d0).sub(v.newY) : xpi.sub(xpi.mul(v.d1).div(v.d0)) ).mul(v.feePerToken).div(FEE_DENOMINATOR) ); } uint256 dy = xpReduced[tokenIndex].sub( getYD(v.preciseA, tokenIndex, xpReduced, v.d1) ); dy = dy.sub(1).div(self.tokenPrecisionMultipliers[tokenIndex]); return (dy, v.newY); } /** * @notice Calculate the price of a token in the pool with given * precision-adjusted balances and a particular D. * * @dev This is accomplished via solving the invariant iteratively. * See the StableSwap paper and Curve.fi implementation for further details. * * x_1**2 + x1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A) * x_1**2 + b*x_1 = c * x_1 = (x_1**2 + c) / (2*x_1 + b) * * @param a the amplification coefficient * n * (n - 1). See the StableSwap paper for details. * @param tokenIndex Index of token we are calculating for. * @param xp a precision-adjusted set of pool balances. Array should be * the same cardinality as the pool. * @param d the stableswap invariant * @return the price of the token, in the same precision as in xp */ function getYD( uint256 a, uint8 tokenIndex, uint256[] memory xp, uint256 d ) internal pure returns (uint256) { uint256 numTokens = xp.length; require(tokenIndex < numTokens, "Token not found"); uint256 c = d; uint256 s; uint256 nA = a.mul(numTokens); for (uint256 i = 0; i < numTokens; i++) { if (i != tokenIndex) { s = s.add(xp[i]); c = c.mul(d).div(xp[i].mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // c = c * D * D * D * ... overflow! } } c = c.mul(d).mul(A_PRECISION).div(nA.mul(numTokens)); uint256 b = s.add(d.mul(A_PRECISION).div(nA)); uint256 yPrev; uint256 y = d; for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { yPrev = y; y = y.mul(y).add(c).div(y.mul(2).add(b).sub(d)); if (y.within1(yPrev)) { return y; } } revert("Approximation did not converge"); } /** * @notice Get D, the StableSwap invariant, based on a set of balances and a particular A. * @param xp a precision-adjusted set of pool balances. Array should be the same cardinality * as the pool. * @param a the amplification coefficient * n * (n - 1) in A_PRECISION. * See the StableSwap paper for details * @return the invariant, at the precision of the pool */ function getD(uint256[] memory xp, uint256 a) internal pure returns (uint256) { uint256 numTokens = xp.length; uint256 s; for (uint256 i = 0; i < numTokens; i++) { s = s.add(xp[i]); } if (s == 0) { return 0; } uint256 prevD; uint256 d = s; uint256 nA = a.mul(numTokens); for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { uint256 dP = d; for (uint256 j = 0; j < numTokens; j++) { dP = dP.mul(d).div(xp[j].mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // dP = dP * D * D * D * ... overflow! } prevD = d; d = nA.mul(s).div(A_PRECISION).add(dP.mul(numTokens)).mul(d).div( nA.sub(A_PRECISION).mul(d).div(A_PRECISION).add( numTokens.add(1).mul(dP) ) ); if (d.within1(prevD)) { return d; } } // Convergence should occur in 4 loops or less. If this is reached, there may be something wrong // with the pool. If this were to occur repeatedly, LPs should withdraw via `removeLiquidity()` // function which does not rely on D. revert("D does not converge"); } /** * @notice Get D, the StableSwap invariant, based on self Swap struct * @param self Swap struct to read from * @return The invariant, at the precision of the pool */ function getD(Swap storage self) internal view returns (uint256) { return getD(_xp(self), _getAPrecise(self)); } /** * @notice Given a set of balances and precision multipliers, return the * precision-adjusted balances. * * @param balances an array of token balances, in their native precisions. * These should generally correspond with pooled tokens. * * @param precisionMultipliers an array of multipliers, corresponding to * the amounts in the balances array. When multiplied together they * should yield amounts at the pool's precision. * * @return an array of amounts "scaled" to the pool's precision */ function _xp( uint256[] memory balances, uint256[] memory precisionMultipliers ) internal pure returns (uint256[] memory) { uint256 numTokens = balances.length; require( numTokens == precisionMultipliers.length, "Balances must match multipliers" ); uint256[] memory xp = new uint256[](numTokens); for (uint256 i = 0; i < numTokens; i++) { xp[i] = balances[i].mul(precisionMultipliers[i]); } return xp; } /** * @notice Return the precision-adjusted balances of all tokens in the pool * @param self Swap struct to read from * @param balances array of balances to scale * @return balances array "scaled" to the pool's precision, allowing * them to be more easily compared. */ function _xp(Swap storage self, uint256[] memory balances) internal view returns (uint256[] memory) { return _xp(balances, self.tokenPrecisionMultipliers); } /** * @notice Return the precision-adjusted balances of all tokens in the pool * @param self Swap struct to read from * @return the pool balances "scaled" to the pool's precision, allowing * them to be more easily compared. */ function _xp(Swap storage self) internal view returns (uint256[] memory) { return _xp(self.balances, self.tokenPrecisionMultipliers); } /** * @notice Get the virtual price, to help calculate profit * @param self Swap struct to read from * @return the virtual price, scaled to precision of POOL_PRECISION_DECIMALS */ function getVirtualPrice(Swap storage self) external view returns (uint256) { uint256 d = getD(_xp(self), _getAPrecise(self)); uint256 supply = self.lpToken.totalSupply(); if (supply > 0) { return d.mul(10**uint256(ERC20(self.lpToken).decimals())).div(supply); } return 0; } /** * @notice Calculate the new balances of the tokens given the indexes of the token * that is swapped from (FROM) and the token that is swapped to (TO). * This function is used as a helper function to calculate how much TO token * the user should receive on swap. * * @param self Swap struct to read from * @param tokenIndexFrom index of FROM token * @param tokenIndexTo index of TO token * @param x the new total amount of FROM token * @param xp balances of the tokens in the pool * @return the amount of TO token that should remain in the pool */ function getY( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 x, uint256[] memory xp ) internal view returns (uint256) { uint256 numTokens = self.pooledTokens.length; require( tokenIndexFrom != tokenIndexTo, "Can't compare token to itself" ); require( tokenIndexFrom < numTokens && tokenIndexTo < numTokens, "Tokens must be in pool" ); uint256 a = _getAPrecise(self); uint256 d = getD(xp, a); uint256 c = d; uint256 s; uint256 nA = numTokens.mul(a); uint256 _x; for (uint256 i = 0; i < numTokens; i++) { if (i == tokenIndexFrom) { _x = x; } else if (i != tokenIndexTo) { _x = xp[i]; } else { continue; } s = s.add(_x); c = c.mul(d).div(_x.mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // c = c * D * D * D * ... overflow! } c = c.mul(d).mul(A_PRECISION).div(nA.mul(numTokens)); uint256 b = s.add(d.mul(A_PRECISION).div(nA)); uint256 yPrev; uint256 y = d; // iterative approximation for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { yPrev = y; y = y.mul(y).add(c).div(y.mul(2).add(b).sub(d)); if (y.within1(yPrev)) { return y; } } revert("Approximation did not converge"); } /** * @notice Externally calculates a swap between two tokens. * @param self Swap struct to read from * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @return dy the number of tokens the user will get */ function calculateSwap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256 dy) { (dy, ) = _calculateSwap(self, tokenIndexFrom, tokenIndexTo, dx); } /** * @notice Internally calculates a swap between two tokens. * * @dev The caller is expected to transfer the actual amounts (dx and dy) * using the token contracts. * * @param self Swap struct to read from * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @return dy the number of tokens the user will get * @return dyFee the associated fee */ function _calculateSwap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) internal view returns (uint256 dy, uint256 dyFee) { uint256[] memory xp = _xp(self); require( tokenIndexFrom < xp.length && tokenIndexTo < xp.length, "Token index out of range" ); uint256 x = dx.mul(self.tokenPrecisionMultipliers[tokenIndexFrom]).add( xp[tokenIndexFrom] ); uint256 y = getY(self, tokenIndexFrom, tokenIndexTo, x, xp); dy = xp[tokenIndexTo].sub(y).sub(1); dyFee = dy.mul(self.swapFee).div(FEE_DENOMINATOR); dy = dy.sub(dyFee).div(self.tokenPrecisionMultipliers[tokenIndexTo]); } /** * @notice A simple method to calculate amount of each underlying * tokens that is returned upon burning given amount of * LP tokens * * @param account the address that is removing liquidity. required for withdraw fee calculation * @param amount the amount of LP tokens that would to be burned on * withdrawal * @return array of amounts of tokens user will receive */ function calculateRemoveLiquidity( Swap storage self, address account, uint256 amount ) external view returns (uint256[] memory) { return _calculateRemoveLiquidity(self, account, amount); } function _calculateRemoveLiquidity( Swap storage self, address account, uint256 amount ) internal view returns (uint256[] memory) { uint256 totalSupply = self.lpToken.totalSupply(); require(amount <= totalSupply, "Cannot exceed total supply"); uint256 feeAdjustedAmount = amount .mul( FEE_DENOMINATOR.sub(calculateCurrentWithdrawFee(self, account)) ) .div(FEE_DENOMINATOR); uint256[] memory amounts = new uint256[](self.pooledTokens.length); for (uint256 i = 0; i < self.pooledTokens.length; i++) { amounts[i] = self.balances[i].mul(feeAdjustedAmount).div( totalSupply ); } return amounts; } /** * @notice Calculate the fee that is applied when the given user withdraws. * Withdraw fee decays linearly over 4 weeks. * @param user address you want to calculate withdraw fee of * @return current withdraw fee of the user */ function calculateCurrentWithdrawFee(Swap storage self, address user) public view returns (uint256) { uint256 endTime = self.depositTimestamp[user].add(4 weeks); if (endTime > block.timestamp) { uint256 timeLeftover = endTime.sub(block.timestamp); return self .defaultWithdrawFee .mul(self.withdrawFeeMultiplier[user]) .mul(timeLeftover) .div(4 weeks) .div(FEE_DENOMINATOR); } return 0; } /** * @notice A simple method to calculate prices from deposits or * withdrawals, excluding fees but including slippage. This is * helpful as an input into the various "min" parameters on calls * to fight front-running * * @dev This shouldn't be used outside frontends for user estimates. * * @param self Swap struct to read from * @param account address of the account depositing or withdrawing tokens * @param amounts an array of token amounts to deposit or withdrawal, * corresponding to pooledTokens. The amount should be in each * pooled token's native precision. If a token charges a fee on transfers, * use the amount that gets transferred after the fee. * @param deposit whether this is a deposit or a withdrawal * @return if deposit was true, total amount of lp token that will be minted and if * deposit was false, total amount of lp token that will be burned */ function calculateTokenAmount( Swap storage self, address account, uint256[] calldata amounts, bool deposit ) external view returns (uint256) { uint256 numTokens = self.pooledTokens.length; uint256 a = _getAPrecise(self); uint256 d0 = getD(_xp(self, self.balances), a); uint256[] memory balances1 = self.balances; for (uint256 i = 0; i < numTokens; i++) { if (deposit) { balances1[i] = balances1[i].add(amounts[i]); } else { balances1[i] = balances1[i].sub( amounts[i], "Cannot withdraw more than available" ); } } uint256 d1 = getD(_xp(self, balances1), a); uint256 totalSupply = self.lpToken.totalSupply(); if (deposit) { return d1.sub(d0).mul(totalSupply).div(d0); } else { return d0.sub(d1).mul(totalSupply).div(d0).mul(FEE_DENOMINATOR).div( FEE_DENOMINATOR.sub( calculateCurrentWithdrawFee(self, account) ) ); } } /** * @notice return accumulated amount of admin fees of the token with given index * @param self Swap struct to read from * @param index Index of the pooled token * @return admin balance in the token's precision */ function getAdminBalance(Swap storage self, uint256 index) external view returns (uint256) { require(index < self.pooledTokens.length, "Token index out of range"); return self.pooledTokens[index].balanceOf(address(this)).sub( self.balances[index] ); } /** * @notice internal helper function to calculate fee per token multiplier used in * swap fee calculations * @param self Swap struct to read from */ function _feePerToken(Swap storage self) internal view returns (uint256) { return self.swapFee.mul(self.pooledTokens.length).div( self.pooledTokens.length.sub(1).mul(4) ); } /*** STATE MODIFYING FUNCTIONS ***/ /** * @notice swap two tokens in the pool * @param self Swap struct to read from and write to * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell * @param minDy the min amount the user would like to receive, or revert. * @return amount of token user received on swap */ function swap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy ) external returns (uint256) { require( dx <= self.pooledTokens[tokenIndexFrom].balanceOf(msg.sender), "Cannot swap more than you own" ); // Transfer tokens first to see if a fee was charged on transfer uint256 beforeBalance = self.pooledTokens[tokenIndexFrom].balanceOf( address(this) ); self.pooledTokens[tokenIndexFrom].safeTransferFrom( msg.sender, address(this), dx ); // Use the actual transferred amount for AMM math uint256 transferredDx = self .pooledTokens[tokenIndexFrom] .balanceOf(address(this)) .sub(beforeBalance); (uint256 dy, uint256 dyFee) = _calculateSwap( self, tokenIndexFrom, tokenIndexTo, transferredDx ); require(dy >= minDy, "Swap didn't result in min tokens"); uint256 dyAdminFee = dyFee.mul(self.adminFee).div(FEE_DENOMINATOR).div( self.tokenPrecisionMultipliers[tokenIndexTo] ); self.balances[tokenIndexFrom] = self.balances[tokenIndexFrom].add( transferredDx ); self.balances[tokenIndexTo] = self.balances[tokenIndexTo].sub(dy).sub( dyAdminFee ); self.pooledTokens[tokenIndexTo].safeTransfer(msg.sender, dy); emit TokenSwap( msg.sender, transferredDx, dy, tokenIndexFrom, tokenIndexTo ); return dy; } /** * @notice Add liquidity to the pool * @param self Swap struct to read from and write to * @param amounts the amounts of each token to add, in their native precision * @param minToMint the minimum LP tokens adding this amount of liquidity * should mint, otherwise revert. Handy for front-running mitigation * @param merkleProof bytes32 array that will be used to prove the existence of the caller's address in the list of * allowed addresses. If the pool is not in the guarded launch phase, this parameter will be ignored. * @return amount of LP token user received */ function addLiquidity( Swap storage self, uint256[] memory amounts, uint256 minToMint, bytes32[] calldata merkleProof ) external returns (uint256) { require( amounts.length == self.pooledTokens.length, "Amounts must match pooled tokens" ); uint256[] memory fees = new uint256[](self.pooledTokens.length); // current state AddLiquidityInfo memory v = AddLiquidityInfo(0, 0, 0, 0); if (self.lpToken.totalSupply() != 0) { v.d0 = getD(self); } uint256[] memory newBalances = self.balances; for (uint256 i = 0; i < self.pooledTokens.length; i++) { require( self.lpToken.totalSupply() != 0 || amounts[i] > 0, "Must supply all tokens in pool" ); // Transfer tokens first to see if a fee was charged on transfer if (amounts[i] != 0) { uint256 beforeBalance = self.pooledTokens[i].balanceOf( address(this) ); self.pooledTokens[i].safeTransferFrom( msg.sender, address(this), amounts[i] ); // Update the amounts[] with actual transfer amount amounts[i] = self.pooledTokens[i].balanceOf(address(this)).sub( beforeBalance ); } newBalances[i] = self.balances[i].add(amounts[i]); } // invariant after change v.preciseA = _getAPrecise(self); v.d1 = getD(_xp(self, newBalances), v.preciseA); require(v.d1 > v.d0, "D should increase"); // updated to reflect fees and calculate the user's LP tokens v.d2 = v.d1; if (self.lpToken.totalSupply() != 0) { uint256 feePerToken = _feePerToken(self); for (uint256 i = 0; i < self.pooledTokens.length; i++) { uint256 idealBalance = v.d1.mul(self.balances[i]).div(v.d0); fees[i] = feePerToken .mul(idealBalance.difference(newBalances[i])) .div(FEE_DENOMINATOR); self.balances[i] = newBalances[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); newBalances[i] = newBalances[i].sub(fees[i]); } v.d2 = getD(_xp(self, newBalances), v.preciseA); } else { // the initial depositor doesn't pay fees self.balances = newBalances; } uint256 toMint; if (self.lpToken.totalSupply() == 0) { toMint = v.d1; } else { toMint = v.d2.sub(v.d0).mul(self.lpToken.totalSupply()).div(v.d0); } require(toMint >= minToMint, "Couldn't mint min requested"); // mint the user's LP tokens self.lpToken.mint(msg.sender, toMint, merkleProof); emit AddLiquidity( msg.sender, amounts, fees, v.d1, self.lpToken.totalSupply() ); return toMint; } /** * @notice Update the withdraw fee for `user`. If the user is currently * not providing liquidity in the pool, sets to default value. If not, recalculate * the starting withdraw fee based on the last deposit's time & amount relative * to the new deposit. * * @param self Swap struct to read from and write to * @param user address of the user depositing tokens * @param toMint amount of pool tokens to be minted */ function updateUserWithdrawFee( Swap storage self, address user, uint256 toMint ) external { _updateUserWithdrawFee(self, user, toMint); } function _updateUserWithdrawFee( Swap storage self, address user, uint256 toMint ) internal { // If token is transferred to address 0 (or burned), don't update the fee. if (user == address(0)) { return; } if (self.defaultWithdrawFee == 0) { // If current fee is set to 0%, set multiplier to FEE_DENOMINATOR self.withdrawFeeMultiplier[user] = FEE_DENOMINATOR; } else { // Otherwise, calculate appropriate discount based on last deposit amount uint256 currentFee = calculateCurrentWithdrawFee(self, user); uint256 currentBalance = self.lpToken.balanceOf(user); // ((currentBalance * currentFee) + (toMint * defaultWithdrawFee)) * FEE_DENOMINATOR / // ((toMint + currentBalance) * defaultWithdrawFee) self.withdrawFeeMultiplier[user] = currentBalance .mul(currentFee) .add(toMint.mul(self.defaultWithdrawFee)) .mul(FEE_DENOMINATOR) .div(toMint.add(currentBalance).mul(self.defaultWithdrawFee)); } self.depositTimestamp[user] = block.timestamp; } /** * @notice Burn LP tokens to remove liquidity from the pool. * @dev Liquidity can always be removed, even when the pool is paused. * @param self Swap struct to read from and write to * @param amount the amount of LP tokens to burn * @param minAmounts the minimum amounts of each token in the pool * acceptable for this burn. Useful as a front-running mitigation * @return amounts of tokens the user received */ function removeLiquidity( Swap storage self, uint256 amount, uint256[] calldata minAmounts ) external returns (uint256[] memory) { require(amount <= self.lpToken.balanceOf(msg.sender), ">LP.balanceOf"); require( minAmounts.length == self.pooledTokens.length, "minAmounts must match poolTokens" ); uint256[] memory amounts = _calculateRemoveLiquidity( self, msg.sender, amount ); for (uint256 i = 0; i < amounts.length; i++) { require(amounts[i] >= minAmounts[i], "amounts[i] < minAmounts[i]"); self.balances[i] = self.balances[i].sub(amounts[i]); self.pooledTokens[i].safeTransfer(msg.sender, amounts[i]); } self.lpToken.burnFrom(msg.sender, amount); emit RemoveLiquidity(msg.sender, amounts, self.lpToken.totalSupply()); return amounts; } /** * @notice Remove liquidity from the pool all in one token. * @param self Swap struct to read from and write to * @param tokenAmount the amount of the lp tokens to burn * @param tokenIndex the index of the token you want to receive * @param minAmount the minimum amount to withdraw, otherwise revert * @return amount chosen token that user received */ function removeLiquidityOneToken( Swap storage self, uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount ) external returns (uint256) { uint256 totalSupply = self.lpToken.totalSupply(); uint256 numTokens = self.pooledTokens.length; require( tokenAmount <= self.lpToken.balanceOf(msg.sender), ">LP.balanceOf" ); require(tokenIndex < numTokens, "Token not found"); uint256 dyFee; uint256 dy; (dy, dyFee) = calculateWithdrawOneToken( self, msg.sender, tokenAmount, tokenIndex ); require(dy >= minAmount, "dy < minAmount"); self.balances[tokenIndex] = self.balances[tokenIndex].sub( dy.add(dyFee.mul(self.adminFee).div(FEE_DENOMINATOR)) ); self.lpToken.burnFrom(msg.sender, tokenAmount); self.pooledTokens[tokenIndex].safeTransfer(msg.sender, dy); emit RemoveLiquidityOne( msg.sender, tokenAmount, totalSupply, tokenIndex, dy ); return dy; } /** * @notice Remove liquidity from the pool, weighted differently than the * pool's current balances. * * @param self Swap struct to read from and write to * @param amounts how much of each token to withdraw * @param maxBurnAmount the max LP token provider is willing to pay to * remove liquidity. Useful as a front-running mitigation. * @return actual amount of LP tokens burned in the withdrawal */ function removeLiquidityImbalance( Swap storage self, uint256[] memory amounts, uint256 maxBurnAmount ) public returns (uint256) { require( amounts.length == self.pooledTokens.length, "Amounts should match pool tokens" ); require( maxBurnAmount <= self.lpToken.balanceOf(msg.sender) && maxBurnAmount != 0, ">LP.balanceOf" ); RemoveLiquidityImbalanceInfo memory v = RemoveLiquidityImbalanceInfo( 0, 0, 0, 0 ); uint256 tokenSupply = self.lpToken.totalSupply(); uint256 feePerToken = _feePerToken(self); uint256[] memory balances1 = self.balances; v.preciseA = _getAPrecise(self); v.d0 = getD(_xp(self), v.preciseA); for (uint256 i = 0; i < self.pooledTokens.length; i++) { balances1[i] = balances1[i].sub( amounts[i], "Cannot withdraw more than available" ); } v.d1 = getD(_xp(self, balances1), v.preciseA); uint256[] memory fees = new uint256[](self.pooledTokens.length); for (uint256 i = 0; i < self.pooledTokens.length; i++) { uint256 idealBalance = v.d1.mul(self.balances[i]).div(v.d0); uint256 difference = idealBalance.difference(balances1[i]); fees[i] = feePerToken.mul(difference).div(FEE_DENOMINATOR); self.balances[i] = balances1[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); balances1[i] = balances1[i].sub(fees[i]); } v.d2 = getD(_xp(self, balances1), v.preciseA); uint256 tokenAmount = v.d0.sub(v.d2).mul(tokenSupply).div(v.d0); require(tokenAmount != 0, "Burnt amount cannot be zero"); tokenAmount = tokenAmount.add(1).mul(FEE_DENOMINATOR).div( FEE_DENOMINATOR.sub(calculateCurrentWithdrawFee(self, msg.sender)) ); require(tokenAmount <= maxBurnAmount, "tokenAmount > maxBurnAmount"); self.lpToken.burnFrom(msg.sender, tokenAmount); for (uint256 i = 0; i < self.pooledTokens.length; i++) { self.pooledTokens[i].safeTransfer(msg.sender, amounts[i]); } emit RemoveLiquidityImbalance( msg.sender, amounts, fees, v.d1, tokenSupply.sub(tokenAmount) ); return tokenAmount; } /** * @notice withdraw all admin fees to a given address * @param self Swap struct to withdraw fees from * @param to Address to send the fees to */ function withdrawAdminFees(Swap storage self, address to) external { for (uint256 i = 0; i < self.pooledTokens.length; i++) { IERC20 token = self.pooledTokens[i]; uint256 balance = token.balanceOf(address(this)).sub( self.balances[i] ); if (balance != 0) { token.safeTransfer(to, balance); } } } /** * @notice Sets the admin fee * @dev adminFee cannot be higher than 100% of the swap fee * @param self Swap struct to update * @param newAdminFee new admin fee to be applied on future transactions */ function setAdminFee(Swap storage self, uint256 newAdminFee) external { require(newAdminFee <= MAX_ADMIN_FEE, "Fee is too high"); self.adminFee = newAdminFee; emit NewAdminFee(newAdminFee); } /** * @notice update the swap fee * @dev fee cannot be higher than 1% of each swap * @param self Swap struct to update * @param newSwapFee new swap fee to be applied on future transactions */ function setSwapFee(Swap storage self, uint256 newSwapFee) external { require(newSwapFee <= MAX_SWAP_FEE, "Fee is too high"); self.swapFee = newSwapFee; emit NewSwapFee(newSwapFee); } /** * @notice update the default withdraw fee. This also affects deposits made in the past as well. * @param self Swap struct to update * @param newWithdrawFee new withdraw fee to be applied */ function setDefaultWithdrawFee(Swap storage self, uint256 newWithdrawFee) external { require(newWithdrawFee <= MAX_WITHDRAW_FEE, "Fee is too high"); self.defaultWithdrawFee = newWithdrawFee; emit NewWithdrawFee(newWithdrawFee); } /** * @notice Start ramping up or down A parameter towards given futureA_ and futureTime_ * Checks if the change is too rapid, and commits the new A value only when it falls under * the limit range. * @param self Swap struct to update * @param futureA_ the new A to ramp towards * @param futureTime_ timestamp when the new A should be reached */ function rampA( Swap storage self, uint256 futureA_, uint256 futureTime_ ) external { require( block.timestamp >= self.initialATime.add(1 days), "Wait 1 day before starting ramp" ); require( futureTime_ >= block.timestamp.add(MIN_RAMP_TIME), "Insufficient ramp time" ); require( futureA_ > 0 && futureA_ < MAX_A, "futureA_ must be > 0 and < MAX_A" ); uint256 initialAPrecise = _getAPrecise(self); uint256 futureAPrecise = futureA_.mul(A_PRECISION); if (futureAPrecise < initialAPrecise) { require( futureAPrecise.mul(MAX_A_CHANGE) >= initialAPrecise, "futureA_ is too small" ); } else { require( futureAPrecise <= initialAPrecise.mul(MAX_A_CHANGE), "futureA_ is too large" ); } self.initialA = initialAPrecise; self.futureA = futureAPrecise; self.initialATime = block.timestamp; self.futureATime = futureTime_; emit RampA( initialAPrecise, futureAPrecise, block.timestamp, futureTime_ ); } /** * @notice Stops ramping A immediately. Once this function is called, rampA() * cannot be called for another 24 hours * @param self Swap struct to update */ function stopRampA(Swap storage self) external { require(self.futureATime > block.timestamp, "Ramp is already stopped"); uint256 currentA = _getAPrecise(self); self.initialA = currentA; self.futureA = currentA; self.initialATime = block.timestamp; self.futureATime = block.timestamp; emit StopRampA(currentA, block.timestamp); } }
// SPDX-License-Identifier: MIT // https://etherscan.io/address/0xC28DF698475dEC994BE00C9C9D8658A548e6304F#code pragma solidity 0.6.12; import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "../interfaces/ISwapGuarded.sol"; /** * @title Liquidity Provider Token * @notice This token is an ERC20 detailed token with added capability to be minted by the owner. * It is used to represent user's shares when providing liquidity to swap contracts. */ contract LPTokenGuarded is ERC20Burnable, Ownable { using SafeMath for uint256; // Address of the swap contract that owns this LP token. When a user adds liquidity to the swap contract, // they receive a proportionate amount of this LPToken. ISwapGuarded public swap; // Maps user account to total number of LPToken minted by them. Used to limit minting during guarded release phase mapping(address => uint256) public mintedAmounts; /** * @notice Deploys LPToken contract with given name, symbol, and decimals * @dev the caller of this constructor will become the owner of this contract * @param name_ name of this token * @param symbol_ symbol of this token * @param decimals_ number of decimals this token will be based on */ constructor( string memory name_, string memory symbol_, uint8 decimals_ ) public ERC20(name_, symbol_) { _setupDecimals(decimals_); swap = ISwapGuarded(_msgSender()); } /** * @notice Mints the given amount of LPToken to the recipient. During the guarded release phase, the total supply * and the maximum number of the tokens that a single account can mint are limited. * @dev only owner can call this mint function * @param recipient address of account to receive the tokens * @param amount amount of tokens to mint * @param merkleProof the bytes32 array data that is used to prove recipient's address exists in the merkle tree * stored in the allowlist contract. If the pool is not guarded, this parameter is ignored. */ function mint( address recipient, uint256 amount, bytes32[] calldata merkleProof ) external onlyOwner { require(amount != 0, "amount == 0"); // If the pool is in the guarded launch phase, the following checks are done to restrict deposits. // 1. Check if the given merkleProof corresponds to the recipient's address in the merkle tree stored in the // allowlist contract. If the account has been already verified, merkleProof is ignored. // 2. Limit the total number of this LPToken minted to recipient as defined by the allowlist contract. // 3. Limit the total supply of this LPToken as defined by the allowlist contract. if (swap.isGuarded()) { IAllowlist allowlist = swap.getAllowlist(); require( allowlist.verifyAddress(recipient, merkleProof), "Invalid merkle proof" ); uint256 totalMinted = mintedAmounts[recipient].add(amount); require( totalMinted <= allowlist.getPoolAccountLimit(address(swap)), "account deposit limit" ); require( totalSupply().add(amount) <= allowlist.getPoolCap(address(swap)), "pool total supply limit" ); mintedAmounts[recipient] = totalMinted; } _mint(recipient, amount); } /** * @dev Overrides ERC20._beforeTokenTransfer() which get called on every transfers including * minting and burning. This ensures that swap.updateUserWithdrawFees are called everytime. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal override(ERC20) { super._beforeTokenTransfer(from, to, amount); swap.updateUserWithdrawFee(to, amount); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../../utils/Context.sol"; import "./ERC20.sol"; /** * @dev Extension of {ERC20} that allows token holders to destroy both their own * tokens and those that they have an allowance for, in a way that can be * recognized off-chain (via event analysis). */ abstract contract ERC20Burnable is Context, ERC20 { using SafeMath for uint256; /** * @dev Destroys `amount` tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 amount) public virtual { _burn(_msgSender(), amount); } /** * @dev Destroys `amount` tokens from `account`, deducting from the caller's * allowance. * * See {ERC20-_burn} and {ERC20-allowance}. * * Requirements: * * - the caller must have allowance for ``accounts``'s tokens of at least * `amount`. */ function burnFrom(address account, uint256 amount) public virtual { uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance"); _approve(account, _msgSender(), decreasedAllowance); _burn(account, amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "./IAllowlist.sol"; interface ISwapGuarded { // pool data view functions function getA() external view returns (uint256); function getAllowlist() external view returns (IAllowlist); function getToken(uint8 index) external view returns (IERC20); function getTokenIndex(address tokenAddress) external view returns (uint8); function getTokenBalance(uint8 index) external view returns (uint256); function getVirtualPrice() external view returns (uint256); function isGuarded() external view returns (bool); // min return calculation functions function calculateSwap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256); function calculateTokenAmount(uint256[] calldata amounts, bool deposit) external view returns (uint256); function calculateRemoveLiquidity(uint256 amount) external view returns (uint256[] memory); function calculateRemoveLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256 availableTokenAmount); // state modifying functions function swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external returns (uint256); function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline, bytes32[] calldata merkleProof ) 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); // withdraw fee update function function updateUserWithdrawFee(address recipient, uint256 transferAmount) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/cryptography/MerkleProof.sol"; import "../interfaces/IAllowlist.sol"; /** * @title Allowlist * @notice This contract is a registry holding information about how much each swap contract should * contain upto. Swap.sol will rely on this contract to determine whether the pool cap is reached and * also whether a user's deposit limit is reached. */ contract Allowlist is Ownable, IAllowlist { using SafeMath for uint256; // Represents the root node of merkle tree containing a list of eligible addresses bytes32 public merkleRoot; // Maps pool address -> maximum total supply mapping(address => uint256) private poolCaps; // Maps pool address -> maximum amount of pool token mintable per account mapping(address => uint256) private accountLimits; // Maps account address -> boolean value indicating whether it has been checked and verified against the merkle tree mapping(address => bool) private verified; event PoolCap(address indexed poolAddress, uint256 poolCap); event PoolAccountLimit(address indexed poolAddress, uint256 accountLimit); event NewMerkleRoot(bytes32 merkleRoot); /** * @notice Creates this contract and sets the PoolCap of 0x0 with uint256(0x54dd1e) for * crude checking whether an address holds this contract. * @param merkleRoot_ bytes32 that represent a merkle root node. This is generated off chain with the list of * qualifying addresses. */ constructor(bytes32 merkleRoot_) public { merkleRoot = merkleRoot_; // This value will be used as a way of crude checking whether an address holds this Allowlist contract // Value 0x54dd1e has no inherent meaning other than it is arbitrary value that checks for // user error. poolCaps[address(0x0)] = uint256(0x54dd1e); emit PoolCap(address(0x0), uint256(0x54dd1e)); emit NewMerkleRoot(merkleRoot_); } /** * @notice Returns the max mintable amount of the lp token per account in given pool address. * @param poolAddress address of the pool * @return max mintable amount of the lp token per account */ function getPoolAccountLimit(address poolAddress) external view override returns (uint256) { return accountLimits[poolAddress]; } /** * @notice Returns the maximum total supply of the pool token for the given pool address. * @param poolAddress address of the pool */ function getPoolCap(address poolAddress) external view override returns (uint256) { return poolCaps[poolAddress]; } /** * @notice Returns true if the given account's existence has been verified against any of the past or * the present merkle tree. Note that if it has been verified in the past, this function will return true * even if the current merkle tree does not contain the account. * @param account the address to check if it has been verified * @return a boolean value representing whether the account has been verified in the past or the present merkle tree */ function isAccountVerified(address account) external view returns (bool) { return verified[account]; } /** * @notice Checks the existence of keccak256(account) as a node in the merkle tree inferred by the merkle root node * stored in this contract. Pools should use this function to check if the given address qualifies for depositing. * If the given account has already been verified with the correct merkleProof, this function will return true when * merkleProof is empty. The verified status will be overwritten if the previously verified user calls this function * with an incorrect merkleProof. * @param account address to confirm its existence in the merkle tree * @param merkleProof data that is used to prove the existence of given parameters. This is generated * during the creation of the merkle tree. Users should retrieve this data off-chain. * @return a boolean value that corresponds to whether the address with the proof has been verified in the past * or if they exist in the current merkle tree. */ function verifyAddress(address account, bytes32[] calldata merkleProof) external override returns (bool) { if (merkleProof.length != 0) { // Verify the account exists in the merkle tree via the MerkleProof library bytes32 node = keccak256(abi.encodePacked(account)); if (MerkleProof.verify(merkleProof, merkleRoot, node)) { verified[account] = true; return true; } } return verified[account]; } // ADMIN FUNCTIONS /** * @notice Sets the account limit of allowed deposit amounts for the given pool * @param poolAddress address of the pool * @param accountLimit the max number of the pool token a single user can mint */ function setPoolAccountLimit(address poolAddress, uint256 accountLimit) external onlyOwner { require(poolAddress != address(0x0), "0x0 is not a pool address"); accountLimits[poolAddress] = accountLimit; emit PoolAccountLimit(poolAddress, accountLimit); } /** * @notice Sets the max total supply of LPToken for the given pool address * @param poolAddress address of the pool * @param poolCap the max total supply of the pool token */ function setPoolCap(address poolAddress, uint256 poolCap) external onlyOwner { require(poolAddress != address(0x0), "0x0 is not a pool address"); poolCaps[poolAddress] = poolCap; emit PoolCap(poolAddress, poolCap); } /** * @notice Updates the merkle root that is stored in this contract. This can only be called by * the owner. If more addresses are added to the list, a new merkle tree and a merkle root node should be generated, * and merkleRoot should be updated accordingly. * @param merkleRoot_ a new merkle root node that contains a list of deposit allowed addresses */ function updateMerkleRoot(bytes32 merkleRoot_) external onlyOwner { merkleRoot = merkleRoot_; emit NewMerkleRoot(merkleRoot_); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev These functions deal with verification of Merkle trees (hash trees), */ library MerkleProof { /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { bytes32 proofElement = proof[i]; if (computedHash <= proofElement) { // Hash(current computed hash + current element of the proof) computedHash = keccak256(abi.encodePacked(computedHash, proofElement)); } else { // Hash(current element of the proof + current computed hash) computedHash = keccak256(abi.encodePacked(proofElement, computedHash)); } } // Check if the computed hash (root) is equal to the provided root return computedHash == root; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import "./OwnerPausable.sol"; import "./SwapUtilsGuarded.sol"; import "../MathUtils.sol"; import "./Allowlist.sol"; /** * @title Swap - A StableSwap implementation in solidity. * @notice This contract is responsible for custody of closely pegged assets (eg. group of stablecoins) * and automatic market making system. Users become an LP (Liquidity Provider) by depositing their tokens * in desired ratios for an exchange of the pool token that represents their share of the pool. * Users can burn pool tokens and withdraw their share of token(s). * * Each time a swap between the pooled tokens happens, a set fee incurs which effectively gets * distributed to the LPs. * * In case of emergencies, admin can pause additional deposits, swaps, or single-asset withdraws - which * stops the ratio of the tokens in the pool from changing. * Users can always withdraw their tokens via multi-asset withdraws. * * @dev Most of the logic is stored as a library `SwapUtils` for the sake of reducing contract's * deployment size. */ contract SwapGuarded is OwnerPausable, ReentrancyGuard { using SafeERC20 for IERC20; using SafeMath for uint256; using MathUtils for uint256; using SwapUtilsGuarded for SwapUtilsGuarded.Swap; // Struct storing data responsible for automatic market maker functionalities. In order to // access this data, this contract uses SwapUtils library. For more details, see SwapUtilsGuarded.sol SwapUtilsGuarded.Swap public swapStorage; // Address to allowlist contract that holds information about maximum totaly supply of lp tokens // and maximum mintable amount per user address. As this is immutable, this will become a constant // after initialization. IAllowlist private immutable allowlist; // Boolean value that notates whether this pool is guarded or not. When isGuarded is true, // addLiquidity function will be restricted by limits defined in allowlist contract. bool private guarded = true; // Maps token address to an index in the pool. Used to prevent duplicate tokens in the pool. // getTokenIndex function also relies on this mapping to retrieve token index. mapping(address => uint8) private tokenIndexes; /*** EVENTS ***/ // events replicated from SwapUtils to make the ABI easier for dumb // clients event TokenSwap( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); event AddLiquidity( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event RemoveLiquidity( address indexed provider, uint256[] tokenAmounts, uint256 lpTokenSupply ); event RemoveLiquidityOne( address indexed provider, uint256 lpTokenAmount, uint256 lpTokenSupply, uint256 boughtId, uint256 tokensBought ); event RemoveLiquidityImbalance( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event NewAdminFee(uint256 newAdminFee); event NewSwapFee(uint256 newSwapFee); event NewWithdrawFee(uint256 newWithdrawFee); event RampA( uint256 oldA, uint256 newA, uint256 initialTime, uint256 futureTime ); event StopRampA(uint256 currentA, uint256 time); /** * @notice Deploys this Swap contract with given parameters as default * values. This will also deploy a LPToken that represents users * LP position. The owner of LPToken will be this contract - which means * only this contract is allowed to mint new tokens. * * @param _pooledTokens an array of ERC20s this pool will accept * @param decimals the decimals to use for each pooled token, * eg 8 for WBTC. Cannot be larger than POOL_PRECISION_DECIMALS * @param lpTokenName the long-form name of the token to be deployed * @param lpTokenSymbol the short symbol for the token to be deployed * @param _a the amplification coefficient * n * (n - 1). See the * StableSwap paper for details * @param _fee default swap fee to be initialized with * @param _adminFee default adminFee to be initialized with * @param _withdrawFee default withdrawFee to be initialized with * @param _allowlist address of allowlist contract for guarded launch */ constructor( IERC20[] memory _pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 _a, uint256 _fee, uint256 _adminFee, uint256 _withdrawFee, IAllowlist _allowlist ) public OwnerPausable() ReentrancyGuard() { // Check _pooledTokens and precisions parameter require(_pooledTokens.length > 1, "_pooledTokens.length <= 1"); require(_pooledTokens.length <= 32, "_pooledTokens.length > 32"); require( _pooledTokens.length == decimals.length, "_pooledTokens decimals mismatch" ); uint256[] memory precisionMultipliers = new uint256[](decimals.length); for (uint8 i = 0; i < _pooledTokens.length; i++) { if (i > 0) { // Check if index is already used. Check if 0th element is a duplicate. require( tokenIndexes[address(_pooledTokens[i])] == 0 && _pooledTokens[0] != _pooledTokens[i], "Duplicate tokens" ); } require( address(_pooledTokens[i]) != address(0), "The 0 address isn't an ERC-20" ); require( decimals[i] <= SwapUtilsGuarded.POOL_PRECISION_DECIMALS, "Token decimals exceeds max" ); precisionMultipliers[i] = 10 ** uint256(SwapUtilsGuarded.POOL_PRECISION_DECIMALS).sub( uint256(decimals[i]) ); tokenIndexes[address(_pooledTokens[i])] = i; } // Check _a, _fee, _adminFee, _withdrawFee, _allowlist parameters require(_a < SwapUtilsGuarded.MAX_A, "_a exceeds maximum"); require(_fee < SwapUtilsGuarded.MAX_SWAP_FEE, "_fee exceeds maximum"); require( _adminFee < SwapUtilsGuarded.MAX_ADMIN_FEE, "_adminFee exceeds maximum" ); require( _withdrawFee < SwapUtilsGuarded.MAX_WITHDRAW_FEE, "_withdrawFee exceeds maximum" ); require( _allowlist.getPoolCap(address(0x0)) == uint256(0x54dd1e), "Allowlist check failed" ); // Initialize swapStorage struct swapStorage.lpToken = new LPTokenGuarded( lpTokenName, lpTokenSymbol, SwapUtilsGuarded.POOL_PRECISION_DECIMALS ); swapStorage.pooledTokens = _pooledTokens; swapStorage.tokenPrecisionMultipliers = precisionMultipliers; swapStorage.balances = new uint256[](_pooledTokens.length); swapStorage.initialA = _a.mul(SwapUtilsGuarded.A_PRECISION); swapStorage.futureA = _a.mul(SwapUtilsGuarded.A_PRECISION); swapStorage.initialATime = 0; swapStorage.futureATime = 0; swapStorage.swapFee = _fee; swapStorage.adminFee = _adminFee; swapStorage.defaultWithdrawFee = _withdrawFee; // Initialize variables related to guarding the initial deposits allowlist = _allowlist; guarded = true; } /*** MODIFIERS ***/ /** * @notice Modifier to check deadline against current timestamp * @param deadline latest timestamp to accept this transaction */ modifier deadlineCheck(uint256 deadline) { require(block.timestamp <= deadline, "Deadline not met"); _; } /*** VIEW FUNCTIONS ***/ /** * @notice Return A, the amplification coefficient * n * (n - 1) * @dev See the StableSwap paper for details * @return A parameter */ function getA() external view returns (uint256) { return swapStorage.getA(); } /** * @notice Return A in its raw precision form * @dev See the StableSwap paper for details * @return A parameter in its raw precision form */ function getAPrecise() external view returns (uint256) { return swapStorage.getAPrecise(); } /** * @notice Return address of the pooled token at given index. Reverts if tokenIndex is out of range. * @param index the index of the token * @return address of the token at given index */ function getToken(uint8 index) public view returns (IERC20) { require(index < swapStorage.pooledTokens.length, "Out of range"); return swapStorage.pooledTokens[index]; } /** * @notice Return the index of the given token address. Reverts if no matching * token is found. * @param tokenAddress address of the token * @return the index of the given token address */ function getTokenIndex(address tokenAddress) external view returns (uint8) { uint8 index = tokenIndexes[tokenAddress]; require( address(getToken(index)) == tokenAddress, "Token does not exist" ); return index; } /** * @notice Reads and returns the address of the allowlist that is set during deployment of this contract * @return the address of the allowlist contract casted to the IAllowlist interface */ function getAllowlist() external view returns (IAllowlist) { return allowlist; } /** * @notice Return timestamp of last deposit of given address * @return timestamp of the last deposit made by the given address */ function getDepositTimestamp(address user) external view returns (uint256) { return swapStorage.getDepositTimestamp(user); } /** * @notice Return current balance of the pooled token at given index * @param index the index of the token * @return current balance of the pooled token at given index with token's native precision */ function getTokenBalance(uint8 index) external view returns (uint256) { require(index < swapStorage.pooledTokens.length, "Index out of range"); return swapStorage.balances[index]; } /** * @notice Get the virtual price, to help calculate profit * @return the virtual price, scaled to the POOL_PRECISION_DECIMALS */ function getVirtualPrice() external view returns (uint256) { return swapStorage.getVirtualPrice(); } /** * @notice Calculate amount of tokens you receive on swap * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell. If the token charges * a fee on transfers, use the amount that gets transferred after the fee. * @return amount of tokens the user will receive */ function calculateSwap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256) { return swapStorage.calculateSwap(tokenIndexFrom, tokenIndexTo, dx); } /** * @notice A simple method to calculate prices from deposits or * withdrawals, excluding fees but including slippage. This is * helpful as an input into the various "min" parameters on calls * to fight front-running * * @dev This shouldn't be used outside frontends for user estimates. * * @param account address that is depositing or withdrawing tokens * @param amounts an array of token amounts to deposit or withdrawal, * corresponding to pooledTokens. The amount should be in each * pooled token's native precision. If a token charges a fee on transfers, * use the amount that gets transferred after the fee. * @param deposit whether this is a deposit or a withdrawal * @return token amount the user will receive */ function calculateTokenAmount( address account, uint256[] calldata amounts, bool deposit ) external view returns (uint256) { return swapStorage.calculateTokenAmount(account, amounts, deposit); } /** * @notice A simple method to calculate amount of each underlying * tokens that is returned upon burning given amount of LP tokens * @param account the address that is withdrawing tokens * @param amount the amount of LP tokens that would be burned on withdrawal * @return array of token balances that the user will receive */ function calculateRemoveLiquidity(address account, uint256 amount) external view returns (uint256[] memory) { return swapStorage.calculateRemoveLiquidity(account, amount); } /** * @notice Calculate the amount of underlying token available to withdraw * when withdrawing via only single token * @param account the address that is withdrawing tokens * @param tokenAmount the amount of LP token to burn * @param tokenIndex index of which token will be withdrawn * @return availableTokenAmount calculated amount of underlying token * available to withdraw */ function calculateRemoveLiquidityOneToken( address account, uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256 availableTokenAmount) { (availableTokenAmount, ) = swapStorage.calculateWithdrawOneToken( account, tokenAmount, tokenIndex ); } /** * @notice Calculate the fee that is applied when the given user withdraws. The withdraw fee * decays linearly over period of 4 weeks. For example, depositing and withdrawing right away * will charge you the full amount of withdraw fee. But withdrawing after 4 weeks will charge you * no additional fees. * @dev returned value should be divided by FEE_DENOMINATOR to convert to correct decimals * @param user address you want to calculate withdraw fee of * @return current withdraw fee of the user */ function calculateCurrentWithdrawFee(address user) external view returns (uint256) { return swapStorage.calculateCurrentWithdrawFee(user); } /** * @notice This function reads the accumulated amount of admin fees of the token with given index * @param index Index of the pooled token * @return admin's token balance in the token's precision */ function getAdminBalance(uint256 index) external view returns (uint256) { return swapStorage.getAdminBalance(index); } /*** STATE MODIFYING FUNCTIONS ***/ /** * @notice Swap two tokens using this pool * @param tokenIndexFrom the token the user wants to swap from * @param tokenIndexTo the token the user wants to swap to * @param dx the amount of tokens the user wants to swap from * @param minDy the min amount the user would like to receive, or revert. * @param deadline latest timestamp to accept this transaction */ function swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.swap(tokenIndexFrom, tokenIndexTo, dx, minDy); } /** * @notice Add liquidity to the pool with given amounts during guarded launch phase. Only users * with valid address and proof can successfully call this function. When this function is called * after the guarded release phase is over, the merkleProof is ignored. * @param amounts the amounts of each token to add, in their native precision * @param minToMint the minimum LP tokens adding this amount of liquidity * should mint, otherwise revert. Handy for front-running mitigation * @param deadline latest timestamp to accept this transaction * @param merkleProof data generated when constructing the allowlist merkle tree. Users can * get this data off chain. Even if the address is in the allowlist, users must include * a valid proof for this call to succeed. If the pool is no longer in the guarded release phase, * this parameter is ignored. * @return amount of LP token user minted and received */ function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline, bytes32[] calldata merkleProof ) external nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.addLiquidity(amounts, minToMint, merkleProof); } /** * @notice Burn LP tokens to remove liquidity from the pool. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @dev Liquidity can always be removed, even when the pool is paused. * @param amount the amount of LP tokens to burn * @param minAmounts the minimum amounts of each token in the pool * acceptable for this burn. Useful as a front-running mitigation * @param deadline latest timestamp to accept this transaction * @return amounts of tokens user received */ function removeLiquidity( uint256 amount, uint256[] calldata minAmounts, uint256 deadline ) external nonReentrant deadlineCheck(deadline) returns (uint256[] memory) { return swapStorage.removeLiquidity(amount, minAmounts); } /** * @notice Remove liquidity from the pool all in one token. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @param tokenAmount the amount of the token you want to receive * @param tokenIndex the index of the token you want to receive * @param minAmount the minimum amount to withdraw, otherwise revert * @param deadline latest timestamp to accept this transaction * @return amount of chosen token user received */ function removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline ) external nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.removeLiquidityOneToken( tokenAmount, tokenIndex, minAmount ); } /** * @notice Remove liquidity from the pool, weighted differently than the * pool's current balances. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @param amounts how much of each token to withdraw * @param maxBurnAmount the max LP token provider is willing to pay to * remove liquidity. Useful as a front-running mitigation. * @param deadline latest timestamp to accept this transaction * @return amount of LP tokens burned */ function removeLiquidityImbalance( uint256[] calldata amounts, uint256 maxBurnAmount, uint256 deadline ) external nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return swapStorage.removeLiquidityImbalance(amounts, maxBurnAmount); } /*** ADMIN FUNCTIONS ***/ /** * @notice Updates the user withdraw fee. This function can only be called by * the pool token. Should be used to update the withdraw fee on transfer of pool tokens. * Transferring your pool token will reset the 4 weeks period. If the recipient is already * holding some pool tokens, the withdraw fee will be discounted in respective amounts. * @param recipient address of the recipient of pool token * @param transferAmount amount of pool token to transfer */ function updateUserWithdrawFee(address recipient, uint256 transferAmount) external { require( msg.sender == address(swapStorage.lpToken), "Only callable by pool token" ); swapStorage.updateUserWithdrawFee(recipient, transferAmount); } /** * @notice Withdraw all admin fees to the contract owner */ function withdrawAdminFees() external onlyOwner { swapStorage.withdrawAdminFees(owner()); } /** * @notice Update the admin fee. Admin fee takes portion of the swap fee. * @param newAdminFee new admin fee to be applied on future transactions */ function setAdminFee(uint256 newAdminFee) external onlyOwner { swapStorage.setAdminFee(newAdminFee); } /** * @notice Update the swap fee to be applied on swaps * @param newSwapFee new swap fee to be applied on future transactions */ function setSwapFee(uint256 newSwapFee) external onlyOwner { swapStorage.setSwapFee(newSwapFee); } /** * @notice Update the withdraw fee. This fee decays linearly over 4 weeks since * user's last deposit. * @param newWithdrawFee new withdraw fee to be applied on future deposits */ function setDefaultWithdrawFee(uint256 newWithdrawFee) external onlyOwner { swapStorage.setDefaultWithdrawFee(newWithdrawFee); } /** * @notice Start ramping up or down A parameter towards given futureA and futureTime * Checks if the change is too rapid, and commits the new A value only when it falls under * the limit range. * @param futureA the new A to ramp towards * @param futureTime timestamp when the new A should be reached */ function rampA(uint256 futureA, uint256 futureTime) external onlyOwner { swapStorage.rampA(futureA, futureTime); } /** * @notice Stop ramping A immediately. Reverts if ramp A is already stopped. */ function stopRampA() external onlyOwner { swapStorage.stopRampA(); } /** * @notice Disables the guarded launch phase, removing any limits on deposit amounts and addresses */ function disableGuard() external onlyOwner { guarded = false; } /** * @notice Reads and returns current guarded status of the pool * @return guarded_ boolean value indicating whether the deposits should be guarded */ function isGuarded() external view returns (bool) { return guarded; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor () internal { _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; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Pausable.sol"; /** * @title OwnerPausable * @notice An ownable contract allows the owner to pause and unpause the * contract without a delay. * @dev Only methods using the provided modifiers will be paused. */ contract OwnerPausable is Ownable, Pausable { /** * @notice Pause the contract. Revert if already paused. */ function pause() external onlyOwner { Pausable._pause(); } /** * @notice Unpause the contract. Revert if already unpaused. */ function unpause() external onlyOwner { Pausable._unpause(); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "./Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract 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 () internal { _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()); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; /** * @title Generic ERC20 token * @notice This contract simulates a generic ERC20 token that is mintable and burnable. */ contract GenericERC20 is ERC20, Ownable { /** * @notice Deploy this contract with given name, symbol, and decimals * @dev the caller of this constructor will become the owner of this contract * @param name_ name of this token * @param symbol_ symbol of this token * @param decimals_ number of decimals this token will be based on */ constructor( string memory name_, string memory symbol_, uint8 decimals_ ) public ERC20(name_, symbol_) { _setupDecimals(decimals_); } /** * @notice Mints given amount of tokens to recipient * @dev only owner can call this mint function * @param recipient address of account to receive the tokens * @param amount amount of tokens to mint */ function mint(address recipient, uint256 amount) external onlyOwner { require(amount != 0, "amount == 0"); _mint(recipient, amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../Swap.sol"; import "./MetaSwapUtils.sol"; /** * @title MetaSwap - A StableSwap implementation in solidity. * @notice This contract is responsible for custody of closely pegged assets (eg. group of stablecoins) * and automatic market making system. Users become an LP (Liquidity Provider) by depositing their tokens * in desired ratios for an exchange of the pool token that represents their share of the pool. * Users can burn pool tokens and withdraw their share of token(s). * * Each time a swap between the pooled tokens happens, a set fee incurs which effectively gets * distributed to the LPs. * * In case of emergencies, admin can pause additional deposits, swaps, or single-asset withdraws - which * stops the ratio of the tokens in the pool from changing. * Users can always withdraw their tokens via multi-asset withdraws. * * MetaSwap is a modified version of Swap that allows Swap's LP token to be utilized in pooling with other tokens. * As an example, if there is a Swap pool consisting of [DAI, USDC, USDT], then a MetaSwap pool can be created * with [sUSD, BaseSwapLPToken] to allow trades between either the LP token or the underlying tokens and sUSD. * Note that when interacting with MetaSwap, users cannot deposit or withdraw via underlying tokens. In that case, * `MetaSwapDeposit.sol` can be additionally deployed to allow interacting with unwrapped representations of the tokens. * * @dev Most of the logic is stored as a library `MetaSwapUtils` for the sake of reducing contract's * deployment size. */ contract MetaSwap is Swap { using MetaSwapUtils for SwapUtils.Swap; MetaSwapUtils.MetaSwap public metaSwapStorage; uint256 constant MAX_UINT256 = 2**256 - 1; /*** EVENTS ***/ // events replicated from SwapUtils to make the ABI easier for dumb // clients event TokenSwapUnderlying( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); /** * @notice Get the virtual price, to help calculate profit * @return the virtual price, scaled to the POOL_PRECISION_DECIMALS */ function getVirtualPrice() external view virtual override returns (uint256) { return MetaSwapUtils.getVirtualPrice(swapStorage, metaSwapStorage); } /** * @notice Calculate amount of tokens you receive on swap * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell. If the token charges * a fee on transfers, use the amount that gets transferred after the fee. * @return amount of tokens the user will receive */ function calculateSwap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view virtual override returns (uint256) { return MetaSwapUtils.calculateSwap( swapStorage, metaSwapStorage, tokenIndexFrom, tokenIndexTo, dx ); } /** * @notice Calculate amount of tokens you receive on swap. For this function, * the token indices are flattened out so that underlying tokens are represented. * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell. If the token charges * a fee on transfers, use the amount that gets transferred after the fee. * @return amount of tokens the user will receive */ function calculateSwapUnderlying( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view virtual returns (uint256) { return MetaSwapUtils.calculateSwapUnderlying( swapStorage, metaSwapStorage, tokenIndexFrom, tokenIndexTo, dx ); } /** * @notice A simple method to calculate prices from deposits or * withdrawals, excluding fees but including slippage. This is * helpful as an input into the various "min" parameters on calls * to fight front-running * * @dev This shouldn't be used outside frontends for user estimates. * * @param amounts an array of token amounts to deposit or withdrawal, * corresponding to pooledTokens. The amount should be in each * pooled token's native precision. If a token charges a fee on transfers, * use the amount that gets transferred after the fee. * @param deposit whether this is a deposit or a withdrawal * @return token amount the user will receive */ function calculateTokenAmount(uint256[] calldata amounts, bool deposit) external view virtual override returns (uint256) { return MetaSwapUtils.calculateTokenAmount( swapStorage, metaSwapStorage, amounts, deposit ); } /** * @notice Calculate the amount of underlying token available to withdraw * when withdrawing via only single token * @param tokenAmount the amount of LP token to burn * @param tokenIndex index of which token will be withdrawn * @return availableTokenAmount calculated amount of underlying token * available to withdraw */ function calculateRemoveLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex ) external view virtual override returns (uint256) { return MetaSwapUtils.calculateWithdrawOneToken( swapStorage, metaSwapStorage, tokenAmount, tokenIndex ); } /*** STATE MODIFYING FUNCTIONS ***/ /** * @notice This overrides Swap's initialize function to prevent initializing * without the address of the base Swap contract. * * @param _pooledTokens an array of ERC20s this pool will accept * @param decimals the decimals to use for each pooled token, * eg 8 for WBTC. Cannot be larger than POOL_PRECISION_DECIMALS * @param lpTokenName the long-form name of the token to be deployed * @param lpTokenSymbol the short symbol for the token to be deployed * @param _a the amplification coefficient * n * (n - 1). See the * StableSwap paper for details * @param _fee default swap fee to be initialized with * @param _adminFee default adminFee to be initialized with */ function initialize( IERC20[] memory _pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 _a, uint256 _fee, uint256 _adminFee, address lpTokenTargetAddress ) public virtual override initializer { revert("use initializeMetaSwap() instead"); } /** * @notice Initializes this MetaSwap contract with the given parameters. * MetaSwap uses an existing Swap pool to expand the available liquidity. * _pooledTokens array should contain the base Swap pool's LP token as * the last element. For example, if there is a Swap pool consisting of * [DAI, USDC, USDT]. Then a MetaSwap pool can be created with [sUSD, BaseSwapLPToken] * as _pooledTokens. * * This will also deploy the LPToken that represents users' * LP position. The owner of LPToken will be this contract - which means * only this contract is allowed to mint new tokens. * * @param _pooledTokens an array of ERC20s this pool will accept. The last * element must be an existing Swap pool's LP token's address. * @param decimals the decimals to use for each pooled token, * eg 8 for WBTC. Cannot be larger than POOL_PRECISION_DECIMALS * @param lpTokenName the long-form name of the token to be deployed * @param lpTokenSymbol the short symbol for the token to be deployed * @param _a the amplification coefficient * n * (n - 1). See the * StableSwap paper for details * @param _fee default swap fee to be initialized with * @param _adminFee default adminFee to be initialized with */ function initializeMetaSwap( IERC20[] memory _pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 _a, uint256 _fee, uint256 _adminFee, address lpTokenTargetAddress, ISwap baseSwap ) external virtual initializer { Swap.initialize( _pooledTokens, decimals, lpTokenName, lpTokenSymbol, _a, _fee, _adminFee, lpTokenTargetAddress ); // MetaSwap initializer metaSwapStorage.baseSwap = baseSwap; metaSwapStorage.baseVirtualPrice = baseSwap.getVirtualPrice(); metaSwapStorage.baseCacheLastUpdated = block.timestamp; // Read all tokens that belong to baseSwap { uint8 i; for (; i < 32; i++) { try baseSwap.getToken(i) returns (IERC20 token) { metaSwapStorage.baseTokens.push(token); token.safeApprove(address(baseSwap), MAX_UINT256); } catch { break; } } require(i > 1, "baseSwap must pool at least 2 tokens"); } // Check the last element of _pooledTokens is owned by baseSwap IERC20 baseLPToken = _pooledTokens[_pooledTokens.length - 1]; require( LPToken(address(baseLPToken)).owner() == address(baseSwap), "baseLPToken is not owned by baseSwap" ); // Pre-approve the baseLPToken to be used by baseSwap baseLPToken.safeApprove(address(baseSwap), MAX_UINT256); } /** * @notice Swap two tokens using this pool * @param tokenIndexFrom the token the user wants to swap from * @param tokenIndexTo the token the user wants to swap to * @param dx the amount of tokens the user wants to swap from * @param minDy the min amount the user would like to receive, or revert. * @param deadline latest timestamp to accept this transaction */ function swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external virtual override nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return MetaSwapUtils.swap( swapStorage, metaSwapStorage, tokenIndexFrom, tokenIndexTo, dx, minDy ); } /** * @notice Swap two tokens using this pool and the base pool. * @param tokenIndexFrom the token the user wants to swap from * @param tokenIndexTo the token the user wants to swap to * @param dx the amount of tokens the user wants to swap from * @param minDy the min amount the user would like to receive, or revert. * @param deadline latest timestamp to accept this transaction */ function swapUnderlying( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external virtual nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return MetaSwapUtils.swapUnderlying( swapStorage, metaSwapStorage, tokenIndexFrom, tokenIndexTo, dx, minDy ); } /** * @notice Add liquidity to the pool with the given amounts of tokens * @param amounts the amounts of each token to add, in their native precision * @param minToMint the minimum LP tokens adding this amount of liquidity * should mint, otherwise revert. Handy for front-running mitigation * @param deadline latest timestamp to accept this transaction * @return amount of LP token user minted and received */ function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline ) external virtual override nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return MetaSwapUtils.addLiquidity( swapStorage, metaSwapStorage, amounts, minToMint ); } /** * @notice Remove liquidity from the pool all in one token. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @param tokenAmount the amount of the token you want to receive * @param tokenIndex the index of the token you want to receive * @param minAmount the minimum amount to withdraw, otherwise revert * @param deadline latest timestamp to accept this transaction * @return amount of chosen token user received */ function removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline ) external virtual override nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return MetaSwapUtils.removeLiquidityOneToken( swapStorage, metaSwapStorage, tokenAmount, tokenIndex, minAmount ); } /** * @notice Remove liquidity from the pool, weighted differently than the * pool's current balances. Withdraw fee that decays linearly * over period of 4 weeks since last deposit will apply. * @param amounts how much of each token to withdraw * @param maxBurnAmount the max LP token provider is willing to pay to * remove liquidity. Useful as a front-running mitigation. * @param deadline latest timestamp to accept this transaction * @return amount of LP tokens burned */ function removeLiquidityImbalance( uint256[] calldata amounts, uint256 maxBurnAmount, uint256 deadline ) external virtual override nonReentrant whenNotPaused deadlineCheck(deadline) returns (uint256) { return MetaSwapUtils.removeLiquidityImbalance( swapStorage, metaSwapStorage, amounts, maxBurnAmount ); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../../MathUtils.sol"; contract TestMathUtils { using MathUtils for uint256; function difference(uint256 a, uint256 b) public pure returns (uint256) { return a.difference(b); } function within1(uint256 a, uint256 b) public pure returns (bool) { return a.within1(b); } }
{ "optimizer": { "enabled": true, "runs": 10000 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "metadata": { "useLiteralContent": true } }
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"fees","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"invariant","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"name":"AddLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newAdminFee","type":"uint256"}],"name":"NewAdminFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newSwapFee","type":"uint256"}],"name":"NewSwapFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"name":"RemoveLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"fees","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"invariant","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"name":"RemoveLiquidityImbalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"lpTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"boughtId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensBought","type":"uint256"}],"name":"RemoveLiquidityOne","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokensSold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensBought","type":"uint256"},{"indexed":false,"internalType":"uint128","name":"soldId","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"boughtId","type":"uint128"}],"name":"TokenSwap","type":"event"},{"inputs":[],"name":"MAX_ADMIN_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SWAP_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POOL_PRECISION_DECIMALS","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
6140b5610026600b82828239805160001a60731461001957fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106101255760003560e01c806371906c2c116100bc578063a5397b221161008b578063e069274211610070578063e0692742146105a3578063e7a4db81146105cf578063f3de03621461060e57610125565b8063a5397b2214610552578063ab3d85441461059b57610125565b806371906c2c1461041057806373fd6b3e1461042d5780637d048160146104b6578063834b4910146104d957610125565b8063467e186c116100f8578063467e186c146103075780634b23603c14610337578063704672761461036d57806370703e4a1461039d57610125565b80630296ab501461012a57806324c5c7511461014857806340370edf1461018357806341b91c261461024e575b600080fd5b610132610616565b6040805160ff9092168252519081900360200190f35b81801561015457600080fd5b506101816004803603604081101561016b57600080fd5b50803590602001356001600160a01b031661061b565b005b81801561018f57600080fd5b5061023c600480360360608110156101a657600080fd5b813591908101906040810160208201356401000000008111156101c857600080fd5b8201836020820111156101da57600080fd5b803590602001918460208302840111640100000000831117156101fc57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550509135925061076a915050565b60408051918252519081900360200190f35b81801561025a57600080fd5b5061023c6004803603606081101561027157600080fd5b8135919081019060408101602082013564010000000081111561029357600080fd5b8201836020820111156102a557600080fd5b803590602001918460208302840111640100000000831117156102c757600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955050913592506110ea915050565b81801561031357600080fd5b506101816004803603604081101561032a57600080fd5b5080359060200135611886565b61023c6004803603608081101561034d57600080fd5b5080359060ff60208201358116916040810135909116906060013561191d565b81801561037957600080fd5b506101816004803603604081101561039057600080fd5b5080359060200135611988565b6103c0600480360360408110156103b357600080fd5b5080359060200135611a20565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156103fc5781810151838201526020016103e4565b505050509050019250505060405180910390f35b61023c6004803603602081101561042657600080fd5b5035611b03565b81801561043957600080fd5b506103c06004803603606081101561045057600080fd5b81359160208101359181019060608101604082013564010000000081111561047757600080fd5b82018360208201111561048957600080fd5b803590602001918460208302840111640100000000831117156104ab57600080fd5b509092509050611bda565b61023c600480360360408110156104cc57600080fd5b5080359060200135612034565b61023c600480360360608110156104ef57600080fd5b8135919081019060408101602082013564010000000081111561051157600080fd5b82018360208201111561052357600080fd5b8035906020019184602083028401116401000000008311171561054557600080fd5b9193509150351515612123565b81801561055e57600080fd5b5061023c600480360360a081101561057557600080fd5b5080359060ff602082013581169160408101359091169060608101359060800135612372565b61023c61275e565b61023c600480360360608110156105b957600080fd5b508035906020810135906040013560ff16612766565b8180156105db57600080fd5b5061023c600480360360808110156105f257600080fd5b5080359060208101359060ff60408201351690606001356127f0565b61023c612bb9565b601281565b60608260070180548060200260200160405190810160405280929190818152602001828054801561067557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610657575b5050505050905060005b815181101561076457600082828151811061069657fe5b60200260200101519050600061073e8660090184815481106106b457fe5b9060005260206000200154836001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561070c57600080fd5b505afa158015610720573d6000803e3d6000fd5b505050506040513d602081101561073657600080fd5b505190612bc2565b9050801561075a5761075a6001600160a01b0383168683612c1f565b505060010161067f565b50505050565b60006060846007018054806020026020016040519081016040528092919081815260200182805480156107c657602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116107a8575b505050505090508051845114610823576040805162461bcd60e51b815260206004820181905260248201527f416d6f756e7473206d757374206d6174636820706f6f6c656420746f6b656e73604482015290519081900360640190fd5b61082b613f0e565b60405180610100016040528060008152602001600081526020016000815260200161085588612ca4565b81526020018760060160009054906101000a90046001600160a01b03166001600160a01b0316815260200160008152602001876009018054806020026020016040519081016040528092919081815260200182805480156108d557602002820191906000526020600020905b8154815260200190600101908083116108c1575b505050505081526020018760080180548060200260200160405190810160405280929190818152602001828054801561092d57602002820191906000526020600020905b815481526020019060010190808311610919575b5050505050815250905080608001516001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561097457600080fd5b505afa158015610988573d6000803e3d6000fd5b505050506040513d602081101561099e57600080fd5b505160a08201819052156109ce576109cb6109c18260c001518360e00151612caf565b8260600151612da6565b81525b6060825167ffffffffffffffff811180156109e857600080fd5b50604051908082528060200260200182016040528015610a12578160200160208202803683370190505b50905060005b8351811015610c685760a0830151151580610a4657506000878281518110610a3c57fe5b6020026020010151115b610a97576040805162461bcd60e51b815260206004820152601e60248201527f4d75737420737570706c7920616c6c20746f6b656e7320696e20706f6f6c0000604482015290519081900360640190fd5b868181518110610aa357fe5b6020026020010151600014610c0c576000848281518110610ac057fe5b60200260200101516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610b1457600080fd5b505afa158015610b28573d6000803e3d6000fd5b505050506040513d6020811015610b3e57600080fd5b50518851909150610b8e90339030908b9086908110610b5957fe5b6020026020010151888681518110610b6d57fe5b60200260200101516001600160a01b0316612f12909392919063ffffffff16565b610bf281868481518110610b9e57fe5b60200260200101516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561070c57600080fd5b888381518110610bfe57fe5b602002602001018181525050505b610c49878281518110610c1b57fe5b60200260200101518460c001518381518110610c3357fe5b6020026020010151612f9a90919063ffffffff16565b828281518110610c5557fe5b6020908102919091010152600101610a18565b50610c84610c7a828460e00151612caf565b8360600151612da6565b60208301819052825110610cdf576040805162461bcd60e51b815260206004820152601160248201527f442073686f756c6420696e637265617365000000000000000000000000000000604482015290519081900360640190fd5b60208201516040830152825160609067ffffffffffffffff81118015610d0457600080fd5b50604051908082528060200260200182016040528015610d2e578160200160208202803683370190505b5090508260a00151600014610ecc576000610d4e89600401548651612ff4565b905060005b8551811015610ea5576000610d9a8660000151610d948860c001518581518110610d7957fe5b6020026020010151896020015161300f90919063ffffffff16565b90613068565b9050610dd56402540be400610d94610dce888681518110610db757fe5b6020026020010151856130cf90919063ffffffff16565b869061300f565b848381518110610de157fe5b602002602001018181525050610e45610e236402540be400610d948e60050154888781518110610e0d57fe5b602002602001015161300f90919063ffffffff16565b868481518110610e2f57fe5b6020026020010151612bc290919063ffffffff16565b8b6009018381548110610e5457fe5b9060005260206000200181905550610e85848381518110610e7157fe5b6020026020010151868481518110610e2f57fe5b858381518110610e9157fe5b602090810291909101015250600101610d53565b50610ec1610eb7848660e00151612caf565b8560600151612da6565b604085015250610ee3565b8151610ee19060098a01906020850190613f5c565b505b60008360a0015160001415610efd57506020830151610f25565b835160a08501516040860151610f229291610d9491610f1c9084612bc2565b9061300f565b90505b86811015610f7a576040805162461bcd60e51b815260206004820152601b60248201527f436f756c646e2774206d696e74206d696e207265717565737465640000000000604482015290519081900360640190fd5b83608001516001600160a01b03166340c10f1933836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015610fd557600080fd5b505af1158015610fe9573d6000803e3d6000fd5b50505050336001600160a01b03167f189c623b666b1b45b83d7178f39b8c087cb09774317ca2f53c2d3c3726f222a289848760200151611036868a60a00151612f9a90919063ffffffff16565b604051808060200180602001858152602001848152602001838103835287818151815260200191508051906020019060200280838360005b8381101561108657818101518382015260200161106e565b50505050905001838103825286818151815260200191508051906020019060200280838360005b838110156110c55781810151838201526020016110ad565b50505050905001965050505050505060405180910390a29450505050505b9392505050565b60006110f4613f0e565b60405180610100016040528060008152602001600081526020016000815260200161111e87612ca4565b81526020018660060160009054906101000a90046001600160a01b03166001600160a01b03168152602001600081526020018660090180548060200260200160405190810160405280929190818152602001828054801561119e57602002820191906000526020600020905b81548152602001906001019080831161118a575b50505050508152602001866008018054806020026020016040519081016040528092919081815260200182805480156111f657602002820191906000526020600020905b8154815260200190600101908083116111e2575b5050505050815250905080608001516001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561123d57600080fd5b505afa158015611251573d6000803e3d6000fd5b505050506040513d602081101561126757600080fd5b505160a082015260078501805460408051602083810282018101909252828152606093909290918301828280156112c757602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116112a9575b505050505090508051855114611324576040805162461bcd60e51b815260206004820181905260248201527f416d6f756e74732073686f756c64206d6174636820706f6f6c20746f6b656e73604482015290519081900360640190fd5b81608001516001600160a01b03166370a08231336040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561137557600080fd5b505afa158015611389573d6000803e3d6000fd5b505050506040513d602081101561139f57600080fd5b505184118015906113af57508315155b611400576040805162461bcd60e51b815260206004820152600d60248201527f3e4c502e62616c616e63654f6600000000000000000000000000000000000000604482015290519081900360640190fd5b600061141187600401548351612ff4565b90506060825167ffffffffffffffff8111801561142d57600080fd5b50604051908082528060200260200182016040528015611457578160200160208202803683370190505b5090506060835167ffffffffffffffff8111801561147457600080fd5b5060405190808252806020026020018201604052801561149e578160200160208202803683370190505b5090506114c06114b68660c001518760e00151612caf565b8660600151612da6565b855260005b8451811015611544576115258982815181106114dd57fe5b602002602001015160405180606001604052806023815260200161405d602391398860c00151848151811061150e57fe5b60200260200101516130e79092919063ffffffff16565b82828151811061153157fe5b60209081029190910101526001016114c5565b506115566114b6828760e00151612caf565b602086015260005b845181101561168357600061159f8760000151610d948960c00151858151811061158457fe5b60200260200101518a6020015161300f90919063ffffffff16565b905060006115c98484815181106115b257fe5b6020026020010151836130cf90919063ffffffff16565b90506115de6402540be400610d94888461300f565b8584815181106115ea57fe5b6020026020010181815250506116226116166402540be400610d948f60050154898881518110610e0d57fe5b858581518110610e2f57fe5b8c600901848154811061163157fe5b906000526020600020018190555061166285848151811061164e57fe5b6020026020010151858581518110610e2f57fe5b84848151811061166e57fe5b6020908102919091010152505060010161155e565b506116956114b6828760e00151612caf565b60408601819052855160a0870151600093506116bb92610d949190610f1c908490612bc2565b90508061170f576040805162461bcd60e51b815260206004820152601b60248201527f4275726e7420616d6f756e742063616e6e6f74206265207a65726f0000000000604482015290519081900360640190fd5b61171a816001612f9a565b905086811115611771576040805162461bcd60e51b815260206004820152601b60248201527f746f6b656e416d6f756e74203e206d61784275726e416d6f756e740000000000604482015290519081900360640190fd5b84608001516001600160a01b03166379cc679033836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b1580156117cc57600080fd5b505af11580156117e0573d6000803e3d6000fd5b5050505060005b845181101561183c57611834338a838151811061180057fe5b602002602001015187848151811061181457fe5b60200260200101516001600160a01b0316612c1f9092919063ffffffff16565b6001016117e7565b50336001600160a01b03167f3631c28b1f9dd213e0319fb167b554d76b6c283a41143eb400a0d1adb1af175589848860200151611036868b60a00151612bc290919063ffffffff16565b6305f5e1008111156118df576040805162461bcd60e51b815260206004820152600f60248201527f46656520697320746f6f20686967680000000000000000000000000000000000604482015290519081900360640190fd5b600482018190556040805182815290517fd88ea5155021c6f8dafa1a741e173f595cdf77ce7c17d43342131d7f06afdfe59181900360200190a15050565b600061197e858585858960090180548060200260200160405190810160405280929190818152602001828054801561197457602002820191906000526020600020905b815481526020019060010190808311611960575b505050505061317e565b5095945050505050565b6402540be4008111156119e2576040805162461bcd60e51b815260206004820152600f60248201527f46656520697320746f6f20686967680000000000000000000000000000000000604482015290519081900360640190fd5b600582018190556040805182815290517fab599d640ca80cde2b09b128a4154a8dfe608cb80f4c9399c8b954b01fd35f389181900360200190a15050565b6060611afa83600901805480602002602001604051908101604052809291908181526020018280548015611a7357602002820191906000526020600020905b815481526020019060010190808311611a5f575b5050505050838560060160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611ac957600080fd5b505afa158015611add573d6000803e3d6000fd5b505050506040513d6020811015611af357600080fd5b5051613326565b90505b92915050565b600080611b20611b1284613403565b611b1b85612ca4565b612da6565b905060008360060160009054906101000a90046001600160a01b031690506000816001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611b7957600080fd5b505afa158015611b8d573d6000803e3d6000fd5b505050506040513d6020811015611ba357600080fd5b505190508015611bcd57611bc381610d9485670de0b6b3a764000061300f565b9350505050611bd5565b600093505050505b919050565b6006840154600785018054604080516020808402820181019092528281526060946001600160a01b03169385939192909190830182828015611c4557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611c27575b50505050509050816001600160a01b03166370a08231336040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015611c9957600080fd5b505afa158015611cad573d6000803e3d6000fd5b505050506040513d6020811015611cc357600080fd5b5051861115611d19576040805162461bcd60e51b815260206004820152600d60248201527f3e4c502e62616c616e63654f6600000000000000000000000000000000000000604482015290519081900360640190fd5b80518414611d6e576040805162461bcd60e51b815260206004820181905260248201527f6d696e416d6f756e7473206d757374206d6174636820706f6f6c546f6b656e73604482015290519081900360640190fd5b606087600901805480602002602001604051908101604052809291908181526020018280548015611dbe57602002820191906000526020600020905b815481526020019060010190808311611daa575b505050505090506000836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611e0057600080fd5b505afa158015611e14573d6000803e3d6000fd5b505050506040513d6020811015611e2a57600080fd5b505190506060611e3b838a84613326565b905060005b8151811015611f1b57888882818110611e5557fe5b90506020020135828281518110611e6857fe5b60200260200101511015611ec3576040805162461bcd60e51b815260206004820152601a60248201527f616d6f756e74735b695d203c206d696e416d6f756e74735b695d000000000000604482015290519081900360640190fd5b611ee6828281518110611ed257fe5b6020026020010151858381518110610e2f57fe5b8b6009018281548110611ef557fe5b9060005260206000200181905550611f133383838151811061180057fe5b600101611e40565b50604080517f79cc6790000000000000000000000000000000000000000000000000000000008152336004820152602481018b905290516001600160a01b038716916379cc679091604480830192600092919082900301818387803b158015611f8357600080fd5b505af1158015611f97573d6000803e3d6000fd5b503392507f88d38ed598fdd809c2bf01ee49cd24b7fdabf379a83d29567952b60324d58cef9150839050611fcb858d612bc2565b6040518080602001838152602001828103825284818151815260200191508051906020019060200280838360005b83811015612011578181015183820152602001611ff9565b50505050905001935050505060405180910390a29450505050505b949350505050565b6007820154600090821061208f576040805162461bcd60e51b815260206004820152601860248201527f546f6b656e20696e646578206f7574206f662072616e67650000000000000000604482015290519081900360640190fd5b611afa8360090183815481106120a157fe5b90600052602060002001548460070184815481106120bb57fe5b60009182526020918290200154604080517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290516001600160a01b03909216926370a0823192602480840193829003018186803b15801561070c57600080fd5b60008061212f86612ca4565b905060608660090180548060200260200160405190810160405280929190818152602001828054801561218157602002820191906000526020600020905b81548152602001906001019080831161216d575b505050505090506060876008018054806020026020016040519081016040528092919081815260200182805480156121d857602002820191906000526020600020905b8154815260200190600101908083116121c4575b5050505050905060006121f46121ee8484612caf565b85612da6565b905060005b83518110156122a35786156122475761222a89898381811061221757fe5b90506020020135858381518110610c3357fe5b84828151811061223657fe5b60200260200101818152505061229b565b61228289898381811061225657fe5b9050602002013560405180606001604052806023815260200161405d6023913986848151811061150e57fe5b84828151811061228e57fe5b6020026020010181815250505b6001016121f9565b5060006122b96122b38585612caf565b86612da6565b905060008a60060160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561230d57600080fd5b505afa158015612321573d6000803e3d6000fd5b505050506040513d602081101561233757600080fd5b5051905087156123605761235383610d9483610f1c8684612bc2565b965050505050505061202c565b61235383610d9483610f1c8387612bc2565b600080866007018660ff168154811061238757fe5b60009182526020918290200154604080517f70a0823100000000000000000000000000000000000000000000000000000000815233600482015290516001600160a01b03909216935083926370a0823192602480840193829003018186803b1580156123f257600080fd5b505afa158015612406573d6000803e3d6000fd5b505050506040513d602081101561241c57600080fd5b5051841115612472576040805162461bcd60e51b815260206004820152601d60248201527f43616e6e6f742073776170206d6f7265207468616e20796f75206f776e000000604482015290519081900360640190fd5b6000816001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156124c157600080fd5b505afa1580156124d5573d6000803e3d6000fd5b505050506040513d60208110156124eb57600080fd5b505190506125046001600160a01b038316333088612f12565b61255581836001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561070c57600080fd5b945050506000806060886009018054806020026020016040519081016040528092919081815260200182805480156125ac57602002820191906000526020600020905b815481526020019060010190808311612598575b505050505090506125c0898989898561317e565b90935091508483101561261a576040805162461bcd60e51b815260206004820181905260248201527f53776170206469646e277420726573756c7420696e206d696e20746f6b656e73604482015290519081900360640190fd5b600061265c8a6008018960ff168154811061263157fe5b9060005260206000200154610d946402540be400610d948e600501548861300f90919063ffffffff16565b905061267187838b60ff1681518110610c3357fe5b8a6009018a60ff168154811061268357fe5b90600052602060002001819055506126ae816126a886858c60ff1681518110610e2f57fe5b90612bc2565b8a6009018960ff16815481106126c057fe5b906000526020600020018190555061270133858c6007018b60ff16815481106126e557fe5b6000918252602090912001546001600160a01b03169190612c1f565b604080518881526020810186905260ff808c16828401528a166060820152905133917fc6c1e0630dbe9130cc068028486c0d118ddcea348550819defd5cb8c257f8a38919081900360800190a25091925050505b95945050505050565b6305f5e10081565b60008061197e8585858860060160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156127bf57600080fd5b505afa1580156127d3573d6000803e3d6000fd5b505050506040513d60208110156127e957600080fd5b50516134b3565b6006840154600785018054604080516020808402820181019092528281526000946001600160a01b031693606093919290919083018282801561285c57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161283e575b50505050509050816001600160a01b03166370a08231336040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156128b057600080fd5b505afa1580156128c4573d6000803e3d6000fd5b505050506040513d60208110156128da57600080fd5b5051861115612930576040805162461bcd60e51b815260206004820152600d60248201527f3e4c502e62616c616e63654f6600000000000000000000000000000000000000604482015290519081900360640190fd5b80518560ff1610612988576040805162461bcd60e51b815260206004820152600f60248201527f546f6b656e206e6f7420666f756e640000000000000000000000000000000000604482015290519081900360640190fd5b6000826001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156129c357600080fd5b505afa1580156129d7573d6000803e3d6000fd5b505050506040513d60208110156129ed57600080fd5b50519050600080612a008a8a8a866134b3565b9150915086821015612a59576040805162461bcd60e51b815260206004820152600e60248201527f6479203c206d696e416d6f756e74000000000000000000000000000000000000604482015290519081900360640190fd5b612ab1612a86612a7f6402540be400610d948e600501548661300f90919063ffffffff16565b8490612f9a565b8b6009018a60ff1681548110612a9857fe5b9060005260206000200154612bc290919063ffffffff16565b8a6009018960ff1681548110612ac357fe5b6000918252602082200191909155604080517f79cc6790000000000000000000000000000000000000000000000000000000008152336004820152602481018c905290516001600160a01b038816926379cc6790926044808201939182900301818387803b158015612b3457600080fd5b505af1158015612b48573d6000803e3d6000fd5b50505050612b603383868b60ff168151811061181457fe5b604080518a81526020810185905260ff8a168183015260608101849052905133917f43fb02998f4e03da2e0e6fff53fdbf0c40a9f45f145dc377fc30615d7d7a8a64919081900360800190a25098975050505050505050565b6402540be40081565b600082821115612c19576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052612c9f908490613515565b505050565b6000611afd826135c6565b81518151606091908114612d0a576040805162461bcd60e51b815260206004820152601f60248201527f42616c616e636573206d757374206d61746368206d756c7469706c6965727300604482015290519081900360640190fd5b60608167ffffffffffffffff81118015612d2357600080fd5b50604051908082528060200260200182016040528015612d4d578160200160208202803683370190505b50905060005b82811015612d9d57612d7e858281518110612d6a57fe5b6020026020010151878381518110610e0d57fe5b828281518110612d8a57fe5b6020908102919091010152600101612d53565b50949350505050565b815160009081805b82811015612de657612ddc868281518110612dc557fe5b602002602001015183612f9a90919063ffffffff16565b9150600101612dae565b5080612df757600092505050611afd565b60008181612e05878661300f565b905060005b610100811015612ec4578260005b87811015612e4757612e3d612e33898d8481518110610e0d57fe5b610d94848861300f565b9150600101612e18565b509293508392612e9b612e7b612e6283610f1c8b6001612f9a565b612e756064610d9489610f1c8a84612bc2565b90612f9a565b610d9486610f1c612e8c868d61300f565b612e756064610d948b8f61300f565b9350612ea7848661365b565b15612ebb5783975050505050505050611afd565b50600101612e0a565b506040805162461bcd60e51b815260206004820152601360248201527f4420646f6573206e6f7420636f6e766572676500000000000000000000000000604482015290519081900360640190fd5b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd00000000000000000000000000000000000000000000000000000000179052610764908590613515565b600082820183811015611afa576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6000611afa6130096004610f1c856001612bc2565b610d9485855b60008261301e57506000611afd565b8282028284828161302b57fe5b0414611afa5760405162461bcd60e51b81526004018080602001828103825260218152602001806140126021913960400191505060405180910390fd5b60008082116130be576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b8183816130c757fe5b049392505050565b6000818311156130e25750808203611afd565b500390565b600081848411156131765760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561313b578181015183820152602001613123565b50505050905090810190601f1680156131685780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6000806060876008018054806020026020016040519081016040528092919081815260200182805480156131d157602002820191906000526020600020905b8154815260200190600101908083116131bd575b5050505050905060606131e48583612caf565b905080518860ff161080156131fc575080518760ff16105b61324d576040805162461bcd60e51b815260206004820152601860248201527f546f6b656e20696e646578206f7574206f662072616e67650000000000000000604482015290519081900360640190fd5b6000613292828a60ff168151811061326157fe5b6020026020010151612e75858c60ff168151811061327b57fe5b60200260200101518a61300f90919063ffffffff16565b905060006132ab6132a28c612ca4565b8b8b8587613672565b90506132c560016126a883868d60ff1681518110610e2f57fe5b95506132e76402540be400610d948d600401548961300f90919063ffffffff16565b9450613316848a60ff16815181106132fb57fe5b6020026020010151610d948789612bc290919063ffffffff16565b9550505050509550959350505050565b60608183111561337d576040805162461bcd60e51b815260206004820152601a60248201527f43616e6e6f742065786365656420746f74616c20737570706c79000000000000604482015290519081900360640190fd5b6060845167ffffffffffffffff8111801561339757600080fd5b506040519080825280602002602001820160405280156133c1578160200160208202803683370190505b50905060005b8551811015612d9d576133e484610d9487898581518110610e0d57fe5b8282815181106133f057fe5b60209081029190910101526001016133c7565b6060611afd8260090180548060200260200160405190810160405280929190818152602001828054801561345657602002820191906000526020600020905b815481526020019060010190808311613442575b5050505050836008018054806020026020016040519081016040528092919081815260200182805480156134a957602002820191906000526020600020905b815481526020019060010190808311613495575b5050505050612caf565b60008060008060006134c789888a896138b8565b8093508194508295505050506000613504846126a88c6008018b60ff16815481106134ee57fe5b600091825260209091200154610d948688612bc2565b939a93995092975050505050505050565b606061356a826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613bb39092919063ffffffff16565b805190915015612c9f5780806020019051602081101561358957600080fd5b5051612c9f5760405162461bcd60e51b815260040180806020018281038252602a815260200180614033602a913960400191505060405180910390fd5b600381015460018201546000919042821115613652576002840154845480831115613624576136196136126135fb8685612bc2565b610d946136084287612bc2565b610f1c8887612bc2565b8290612f9a565b945050505050611bd5565b61361961364b6136348685612bc2565b610d946136414287612bc2565b610f1c8689612bc2565b8290612bc2565b9150611bd59050565b6000600161366984846130cf565b11159392505050565b805160009060ff86811690861614156136d2576040805162461bcd60e51b815260206004820152601d60248201527f43616e277420636f6d7061726520746f6b656e20746f20697473656c66000000604482015290519081900360640190fd5b808660ff161080156136e65750808560ff16105b613737576040805162461bcd60e51b815260206004820152601660248201527f546f6b656e73206d75737420626520696e20706f6f6c00000000000000000000604482015290519081900360640190fd5b60006137438489612da6565b905080600080613753858c61300f565b90506000805b868110156137cc578b60ff168114156137745789915061379e565b8a60ff1681146137995788818151811061378a57fe5b6020026020010151915061379e565b6137c4565b6137a88483612f9a565b93506137c16137b7838961300f565b610d94878961300f565b94505b600101613759565b506137e96137da838861300f565b610d946064610f1c888a61300f565b935060006138066137ff84610d9489606461300f565b8590612f9a565b9050600086815b61010081101561386a5790915081906138406138328a6126a887612e7587600261300f565b610d948a612e75868061300f565b915061384c828461365b565b1561386257509850612755975050505050505050565b60010161380d565b506040805162461bcd60e51b815260206004820152601e60248201527f417070726f78696d6174696f6e20646964206e6f7420636f6e76657267650000604482015290519081900360640190fd5b600080600060606138c888613403565b905080518760ff1610613922576040805162461bcd60e51b815260206004820152601860248201527f546f6b656e20696e646578206f7574206f662072616e67650000000000000000604482015290519081900360640190fd5b61392a613fa7565b6040518060a00160405280600081526020016000815260200160008152602001600081526020016000815250905061396189612ca4565b60808201819052613973908390612da6565b8082526139939061398b908890610d94908b9061300f565b825190612bc2565b60208201528151829060ff8a169081106139a957fe5b6020026020010151871115613a05576040805162461bcd60e51b815260206004820152601a60248201527f5769746864726177206578636565647320617661696c61626c65000000000000604482015290519081900360640190fd5b613a19816080015189848460200151613bc2565b6040820152815160609067ffffffffffffffff81118015613a3957600080fd5b50604051908082528060200260200182016040528015613a63578160200160208202803683370190505b509050613a758a600401548451612ff4565b606083015260005b8351811015613b29576000848281518110613a9457fe5b60200260200101519050613b0961364b6402540be400610d9487606001518f60ff168714613ae257885160208a0151613add91613ad691610d94908a9061300f565b8790612bc2565b610f1c565b610f1c89604001516126a88b60000151610d948d602001518b61300f90919063ffffffff16565b838381518110613b1557fe5b602090810291909101015250600101613a7d565b506000613b52613b4384608001518c858760200151613bc2565b838c60ff1681518110610e2f57fe5b9050613b808b6008018b60ff1681548110613b6957fe5b600091825260209091200154610d94836001612bc2565b9050808360400151858c60ff1681518110613b9757fe5b6020026020010151965096509650505050509450945094915050565b606061202c8484600085613d28565b815160009060ff85168111613c1e576040805162461bcd60e51b815260206004820152600f60248201527f546f6b656e206e6f7420666f756e640000000000000000000000000000000000604482015290519081900360640190fd5b82600080613c2c898561300f565b905060005b84811015613c93578860ff168114613c8b57613c69888281518110613c5257fe5b602002602001015184612f9a90919063ffffffff16565b9250613c88613c7e868a8481518110610e0d57fe5b610d94868a61300f565b93505b600101613c31565b50613cb0613ca1828661300f565b610d946064610f1c878b61300f565b92506000613cc6612a7f83610d948a606461300f565b9050600087815b61010081101561386a579091508190613d00613cf28b6126a887612e7587600261300f565b610d9489612e75868061300f565b9150613d0c828461365b565b15613d205750965061202c95505050505050565b600101613ccd565b606082471015613d695760405162461bcd60e51b8152600401808060200182810382526026815260200180613fec6026913960400191505060405180910390fd5b613d7285613ea2565b613dc3576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b60208310613e2057805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101613de3565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114613e82576040519150601f19603f3d011682016040523d82523d6000602084013e613e87565b606091505b5091509150613e97828286613ea8565b979650505050505050565b3b151590565b60608315613eb75750816110e3565b825115613ec75782518084602001fd5b60405162461bcd60e51b815260206004820181815284516024840152845185939192839260440191908501908083836000831561313b578181015183820152602001613123565b6040518061010001604052806000815260200160008152602001600081526020016000815260200160006001600160a01b031681526020016000815260200160608152602001606081525090565b828054828255906000526020600020908101928215613f97579160200282015b82811115613f97578251825591602001919060010190613f7c565b50613fa3929150613fd6565b5090565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b5b80821115613fa35760008155600101613fd756fe416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656443616e6e6f74207769746864726177206d6f7265207468616e20617661696c61626c65a2646970667358221220060a6187b423dfd039618df2673efa5d77c62c64dfcfa6231e18e380d3d211e664736f6c634300060c0033
Deployed ByteCode Sourcemap
692:36055:45:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3464:50;;;:::i;:::-;;;;;;;;;;;;;;;;;;;35393:455;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;35393:455:45;;;;;;-1:-1:-1;;;;;35393:455:45;;:::i;:::-;;25531:3424;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;25531:3424:45;;-1:-1:-1;;25531:3424:45;;;-1:-1:-1;25531:3424:45;;-1:-1:-1;;25531:3424:45:i;:::-;;;;;;;;;;;;;;;;32552:2663;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;32552:2663:45;;-1:-1:-1;;32552:2663:45;;;-1:-1:-1;32552:2663:45;;-1:-1:-1;;32552:2663:45:i;36533:212::-;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;36533:212:45;;;;;;;:::i;17132:334::-;;;;;;;;;;;;;;;;-1:-1:-1;17132:334:45;;;;;;;;;;;;;;;;;;;;;;;:::i;36086:220::-;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;36086:220:45;;;;;;;:::i;19271:312::-;;;;;;;;;;;;;;;;-1:-1:-1;19271:312:45;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13955:386;;;;;;;;;;;;;;;;-1:-1:-1;13955:386:45;;:::i;29419:1132::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;29419:1132:45;;-1:-1:-1;29419:1132:45;-1:-1:-1;29419:1132:45;:::i;22206:339::-;;;;;;;;;;;;;;;;-1:-1:-1;22206:339:45;;;;;;;:::i;20952:1003::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;20952:1003:45;-1:-1:-1;20952:1003:45;;;;:::i;23445:1579::-;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;23445:1579:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;3782:44::-;;;:::i;4765:387::-;;;;;;;;;;;;;;;;-1:-1:-1;4765:387:45;;;;;;;;;;;;;;:::i;30951:1144::-;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;30951:1144:45;;;;;;;;;;;;;;;;;;;:::i;4069:46::-;;;:::i;3464:50::-;3512:2;3464:50;:::o;35393:455::-;35470:28;35501:4;:17;;35470:48;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;35470:48:45;;;;;;;;;;;;;;;;;;;;;;;35533:9;35528:314;35552:12;:19;35548:1;:23;35528:314;;;35592:12;35607;35620:1;35607:15;;;;;;;;;;;;;;35592:30;;35636:15;35654:82;35706:4;:13;;35720:1;35706:16;;;;;;;;;;;;;;;;35654:5;-1:-1:-1;;;;;35654:15:45;;35678:4;35654:30;;;;;;;;;;;;;-1:-1:-1;;;;;35654:30:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;35654:30:45;;:34;:82::i;:::-;35636:100;-1:-1:-1;35754:12:45;;35750:82;;35786:31;-1:-1:-1;;;;;35786:18:45;;35805:2;35809:7;35786:18;:31::i;:::-;-1:-1:-1;;35573:3:45;;35528:314;;;;35393:455;;;:::o;25531:3424::-;25665:7;25684:28;25715:4;:17;;25684:48;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;25684:48:45;;;;;;;;;;;;;;;;;;;;;;;25781:12;:19;25763:7;:14;:37;25742:116;;;;;-1:-1:-1;;;25742:116:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;25894:28;;:::i;:::-;25925:218;;;;;;;;25958:1;25925:218;;;;25973:1;25925:218;;;;25988:1;25925:218;;;;26003:18;26016:4;26003:12;:18::i;:::-;25925:218;;;;26035:4;:12;;;;;;;;;;-1:-1:-1;;;;;26035:12:45;-1:-1:-1;;;;;25925:218:45;;;;;26061:1;25925:218;;;;26076:4;:13;;25925:218;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;26103:4;:30;;25925:218;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;25894:249;;26169:1;:9;;;-1:-1:-1;;;;;26169:21:45;;:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;26169:23:45;26153:13;;;:39;;;26207:18;26203:104;;26248:48;26253:30;26257:1;:10;;;26269:1;:13;;;26253:3;:30::i;:::-;26285:1;:10;;;26248:4;:48::i;:::-;26241:55;;26203:104;26317:28;26362:12;:19;26348:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;26348:34:45;;26317:65;;26398:9;26393:872;26417:12;:19;26413:1;:23;26393:872;;;26482:13;;;;:18;;;:36;;;26517:1;26504:7;26512:1;26504:10;;;;;;;;;;;;;;:14;26482:36;26457:125;;;;;-1:-1:-1;;;26457:125:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;26678:7;26686:1;26678:10;;;;;;;;;;;;;;26692:1;26678:15;26674:520;;26713:21;26737:12;26750:1;26737:15;;;;;;;;;;;;;;-1:-1:-1;;;;;26737:25:45;;26792:4;26737:78;;;;;;;;;;;;;-1:-1:-1;;;;;26737:78:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;26737:78:45;26954:10;;26737:78;;-1:-1:-1;26833:149:45;;26887:10;;26927:4;;26954:7;;26962:1;;26954:10;;;;;;;;;;;;26833:12;26846:1;26833:15;;;;;;;;;;;;;;-1:-1:-1;;;;;26833:32:45;;;:149;;;;;;:::i;:::-;27082:97;27148:13;27082:12;27095:1;27082:15;;;;;;;;;;;;;;-1:-1:-1;;;;;27082:25:45;;27116:4;27082:40;;;;;;;;;;;;;-1:-1:-1;;;;;27082:40:45;;;;;;;;;;;;;;;;;;;;;;;;;;:97;27069:7;27077:1;27069:10;;;;;;;;;;;;;:110;;;;;26674:520;;27225:29;27243:7;27251:1;27243:10;;;;;;;;;;;;;;27225:1;:10;;;27236:1;27225:13;;;;;;;;;;;;;;:17;;:29;;;;:::i;:::-;27208:11;27220:1;27208:14;;;;;;;;;;;;;;;;;:46;26438:3;;26393:872;;;;27316:49;27321:31;27325:11;27338:1;:13;;;27321:3;:31::i;:::-;27354:1;:10;;;27316:4;:49::i;:::-;27309:4;;;:56;;;27390:4;;-1:-1:-1;27375:41:45;;;;;-1:-1:-1;;;27375:41:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;27504:4;;;;27497;;;:11;27556:19;;27518:21;;27542:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;27542:34:45;;27518:58;;27591:1;:13;;;27608:1;27591:18;27587:849;;27625:19;27647:93;27677:4;:12;;;27707;:19;27647:12;:93::i;:::-;27625:115;;27759:9;27754:490;27778:12;:19;27774:1;:23;27754:490;;;27822:20;27845:33;27873:1;:4;;;27845:23;27854:1;:10;;;27865:1;27854:13;;;;;;;;;;;;;;27845:1;:4;;;:8;;:23;;;;:::i;:::-;:27;;:33::i;:::-;27822:56;;27906:119;3720:6;27906:77;27943:39;27967:11;27979:1;27967:14;;;;;;;;;;;;;;27943:12;:23;;:39;;;;:::i;:::-;27906:11;;:36;:77::i;:119::-;27896:4;27901:1;27896:7;;;;;;;;;;;;;:129;;;;;28062:105;28102:47;3720:6;28102:26;28114:4;:13;;;28102:4;28107:1;28102:7;;;;;;;;;;;;;;:11;;:26;;;;:::i;:47::-;28062:11;28074:1;28062:14;;;;;;;;;;;;;;:18;;:105;;;;:::i;:::-;28043:4;:13;;28057:1;28043:16;;;;;;;;;;;;;;;:124;;;;28202:27;28221:4;28226:1;28221:7;;;;;;;;;;;;;;28202:11;28214:1;28202:14;;;;;;;:27;28185:11;28197:1;28185:14;;;;;;;;;;;;;;;;;:44;-1:-1:-1;27799:3:45;;27754:490;;;;28264:49;28269:31;28273:11;28286:1;:13;;;28269:3;:31::i;:::-;28302:1;:10;;;28264:4;:49::i;:::-;28257:4;;;:56;-1:-1:-1;27587:849:45;;;28398:27;;;;:13;;;;:27;;;;;:::i;:::-;;27587:849;28446:14;28474:1;:13;;;28491:1;28474:18;28470:145;;;-1:-1:-1;28517:4:45;;;;28470:145;;;28599:4;;28580:13;;;;28561:4;;;;:43;;28599:4;28561:33;;:14;;28599:4;28561:8;:14::i;:::-;:18;;:33::i;:43::-;28552:52;;28470:145;28643:9;28633:6;:19;;28625:59;;;;;-1:-1:-1;;;28625:59:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;28732:1;:9;;;-1:-1:-1;;;;;28732:14:45;;28747:10;28759:6;28732:34;;;;;;;;;;;;;-1:-1:-1;;;;;28732:34:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;28808:10;-1:-1:-1;;;;;28782:142:45;;28832:7;28853:4;28871:1;:4;;;28889:25;28907:6;28889:1;:13;;;:17;;:25;;;;:::i;:::-;28782:142;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;28942:6;-1:-1:-1;;;;;25531:3424:45;;;;;;:::o;32552:2663::-;32700:7;32719:28;;:::i;:::-;32750:218;;;;;;;;32783:1;32750:218;;;;32798:1;32750:218;;;;32813:1;32750:218;;;;32828:18;32841:4;32828:12;:18::i;:::-;32750:218;;;;32860:4;:12;;;;;;;;;;-1:-1:-1;;;;;32860:12:45;-1:-1:-1;;;;;32750:218:45;;;;;32886:1;32750:218;;;;32901:4;:13;;32750:218;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;32928:4;:30;;32750:218;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;32719:249;;32994:1;:9;;;-1:-1:-1;;;;;32994:21:45;;:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;32994:23:45;32978:13;;;:39;33059:17;;;33028:48;;;;;32994:23;33028:48;;;;;;;;;;;;;:28;;:48;;33059:17;;33028:48;;33059:17;33028:48;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;33028:48:45;;;;;;;;;;;;;;;;;;;;;;;33126:12;:19;33108:7;:14;:37;33087:116;;;;;-1:-1:-1;;;33087:116:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;33252:1;:9;;;-1:-1:-1;;;;;33252:19:45;;33272:10;33252:31;;;;;;;;;;;;;-1:-1:-1;;;;;33252:31:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;33252:31:45;33235:48;;;;;:86;;-1:-1:-1;33303:18:45;;;33235:86;33214:146;;;;;-1:-1:-1;;;33214:146:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;33371:19;33393:47;33406:4;:12;;;33420;:19;33393:12;:47::i;:::-;33371:69;;33450:21;33488:12;:19;33474:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;33474:34:45;;33450:58;;33532:26;33575:12;:19;33561:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;33561:34:45;;33532:63;;33616:48;33621:30;33625:1;:10;;;33637:1;:13;;;33621:3;:30::i;:::-;33653:1;:10;;;33616:4;:48::i;:::-;33609:55;;:4;33678:224;33702:12;:19;33698:1;:23;33678:224;;;33761:126;33800:7;33808:1;33800:10;;;;;;;;;;;;;;33761:126;;;;;;;;;;;;;;;;;:1;:10;;;33772:1;33761:13;;;;;;;;;;;;;;:17;;:126;;;;;:::i;:::-;33746:9;33756:1;33746:12;;;;;;;;;;;;;;;;;:141;33723:3;;33678:224;;;;33922:47;33927:29;33931:9;33942:1;:13;;;33927:3;:29::i;33922:47::-;33915:4;;;:54;33989:9;33984:489;34008:12;:19;34004:1;:23;33984:489;;;34052:20;34075:33;34103:1;:4;;;34075:23;34084:1;:10;;;34095:1;34084:13;;;;;;;;;;;;;;34075:1;:4;;;:8;;:23;;;;:::i;:33::-;34052:56;;34126:18;34147:37;34171:9;34181:1;34171:12;;;;;;;;;;;;;;34147;:23;;:37;;;;:::i;:::-;34126:58;-1:-1:-1;34212:48:45;3720:6;34212:27;:11;34126:58;34212:15;:27::i;:48::-;34202:4;34207:1;34202:7;;;;;;;;;;;;;:58;;;;;34297:103;34335:47;3720:6;34335:26;34347:4;:13;;;34335:4;34340:1;34335:7;;;;;;;:47;34297:9;34307:1;34297:12;;;;;;;:103;34278:4;:13;;34292:1;34278:16;;;;;;;;;;;;;;;:122;;;;34433:25;34450:4;34455:1;34450:7;;;;;;;;;;;;;;34433:9;34443:1;34433:12;;;;;;;:25;34418:9;34428:1;34418:12;;;;;;;;;;;;;;;;;:40;-1:-1:-1;;34029:3:45;;33984:489;;;;34494:47;34499:29;34503:9;34514:1;:13;;;34499:3;:29::i;34494:47::-;34487:4;;;:54;;;34621:4;;34602:13;;;;34561:19;;-1:-1:-1;34583:43:45;;:33;;34602:13;34583:14;;34621:4;;34583:8;:14::i;:43::-;34561:65;-1:-1:-1;34644:16:45;34636:56;;;;;-1:-1:-1;;;34636:56:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;34716:18;:11;34732:1;34716:15;:18::i;:::-;34702:32;;34768:13;34753:11;:28;;34745:68;;;;;-1:-1:-1;;;34745:68:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;34824:1;:9;;;-1:-1:-1;;;;;34824:18:45;;34843:10;34855:11;34824:43;;;;;;;;;;;;;-1:-1:-1;;;;;34824:43:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;34883:9;34878:127;34902:12;:19;34898:1;:23;34878:127;;;34942:52;34971:10;34983:7;34991:1;34983:10;;;;;;;;;;;;;;34942:12;34955:1;34942:15;;;;;;;;;;;;;;-1:-1:-1;;;;;34942:28:45;;;:52;;;;;:::i;:::-;34923:3;;34878:127;;;;35058:10;-1:-1:-1;;;;;35020:159:45;;35082:7;35103:4;35121:1;:4;;;35139:30;35157:11;35139:1;:13;;;:17;;:30;;;;:::i;36533:212::-;3821:5;36619:10;:26;;36611:54;;;;;-1:-1:-1;;;36611:54:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;36675:12;;;:25;;;36716:22;;;;;;;;;;;;;;;;;36533:212;;:::o;17132:334::-;17289:10;17320:139;17348:4;17366:14;17394:12;17420:2;17436:4;:13;;17320:139;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:14;:139::i;:::-;-1:-1:-1;17311:148:45;17132:334;-1:-1:-1;;;;;17132:334:45:o;36086:220::-;4109:6;36174:11;:28;;36166:56;;;;;-1:-1:-1;;;36166:56:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;36232:13;;;:27;;;36275:24;;;;;;;;;;;;;;;;;36086:220;;:::o;19271:312::-;19387:16;19438:138;19481:4;:13;;19438:138;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19512:6;19536:4;:12;;;;;;;;;;-1:-1:-1;;;;;19536:12:45;-1:-1:-1;;;;;19536:24:45;;:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;19536:26:45;19438:25;:138::i;:::-;19419:157;;19271:312;;;;;:::o;13955:386::-;14046:7;14069:9;14081:35;14086:9;14090:4;14086:3;:9::i;:::-;14097:18;14110:4;14097:12;:18::i;:::-;14081:4;:35::i;:::-;14069:47;;14126:15;14144:4;:12;;;;;;;;;;-1:-1:-1;;;;;14144:12:45;14126:30;;14166:14;14183:7;-1:-1:-1;;;;;14183:19:45;;:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;14183:21:45;;-1:-1:-1;14218:10:45;;14214:103;;14251:55;14299:6;14251:43;:1;14257:36;14251:5;:43::i;:55::-;14244:62;;;;;;;14214:103;14333:1;14326:8;;;;;13955:386;;;;:::o;29419:1132::-;29604:12;;;;29657:17;;;29626:48;;;;;;;;;;;;;;;;;;;29558:16;;-1:-1:-1;;;;;29604:12:45;;29558:16;;29626:48;;29657:17;;29626:48;;;29657:17;29626:48;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;29626:48:45;;;;;;;;;;;;;;;;;;;;;;;29702:7;-1:-1:-1;;;;;29702:17:45;;29720:10;29702:29;;;;;;;;;;;;;-1:-1:-1;;;;;29702:29:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;29702:29:45;29692:39;;;29684:65;;;;;-1:-1:-1;;;29684:65:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;29801:19;;29780:40;;29759:119;;;;;-1:-1:-1;;;29759:119:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;29889:25;29917:4;:13;;29889:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;29940:19;29962:7;-1:-1:-1;;;;;29962:19:45;;:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;29962:21:45;;-1:-1:-1;29994:24:45;30021:102;30060:8;30082:6;29962:21;30021:25;:102::i;:::-;29994:129;;30139:9;30134:262;30158:7;:14;30154:1;:18;30134:262;;;30215:10;;30226:1;30215:13;;;;;;;;;;;;;30201:7;30209:1;30201:10;;;;;;;;;;;;;;:27;;30193:66;;;;;-1:-1:-1;;;30193:66:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;30292:27;30308:7;30316:1;30308:10;;;;;;;;;;;;;;30292:8;30301:1;30292:11;;;;;;;:27;30273:4;:13;;30287:1;30273:16;;;;;;;;;;;;;;;:46;;;;30333:52;30362:10;30374:7;30382:1;30374:10;;;;;;;30333:52;30174:3;;30134:262;;;-1:-1:-1;30406:36:45;;;;;;30423:10;30406:36;;;;;;;;;;;;-1:-1:-1;;;;;30406:16:45;;;;;:36;;;;;-1:-1:-1;;30406:36:45;;;;;;;-1:-1:-1;30406:16:45;:36;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;30474:10:45;;-1:-1:-1;30458:61:45;;-1:-1:-1;30486:7:45;;-1:-1:-1;30495:23:45;:11;30511:6;30495:15;:23::i;:::-;30458:61;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30537:7;-1:-1:-1;;;;;29419:1132:45;;;;;;;:::o;22206:339::-;22351:17;;;:24;22312:7;;22343:32;;22335:69;;;;;-1:-1:-1;;;22335:69:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;22433:105;22504:4;:13;;22518:5;22504:20;;;;;;;;;;;;;;;;22433:4;:17;;22451:5;22433:24;;;;;;;;;;;;;;;;;;;:49;;;;;;22476:4;22433:49;;;;;;-1:-1:-1;;;;;22433:24:45;;;;:34;;:49;;;;;;;;;;:24;:49;;;;;;;;;;20952:1003;21096:7;21115:9;21127:18;21140:4;21127:12;:18::i;:::-;21115:30;;21155:25;21183:4;:13;;21155:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;21206:28;21237:4;:30;;21206:61;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;21278:10;21291:35;21296:26;21300:8;21310:11;21296:3;:26::i;:::-;21324:1;21291:4;:35::i;:::-;21278:48;;21341:9;21336:334;21360:8;:15;21356:1;:19;21336:334;;;21400:7;21396:264;;;21441:27;21457:7;;21465:1;21457:10;;;;;;;;;;;;;21441:8;21450:1;21441:11;;;;;;;:27;21427:8;21436:1;21427:11;;;;;;;;;;;;;:41;;;;;21396:264;;;21521:124;21558:7;;21566:1;21558:10;;;;;;;;;;;;;21521:124;;;;;;;;;;;;;;;;;:8;21530:1;21521:11;;;;;;;:124;21507:8;21516:1;21507:11;;;;;;;;;;;;;:138;;;;;21396:264;21377:3;;21336:334;;;;21679:10;21692:35;21697:26;21701:8;21711:11;21697:3;:26::i;:::-;21725:1;21692:4;:35::i;:::-;21679:48;;21737:19;21759:4;:12;;;;;;;;;;-1:-1:-1;;;;;21759:12:45;-1:-1:-1;;;;;21759:24:45;;:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;21759:26:45;;-1:-1:-1;21796:153:45;;;;21830:35;21862:2;21830:27;21845:11;21830:10;:2;21862;21830:6;:10::i;:35::-;21823:42;;;;;;;;;;21796:153;21903:35;21935:2;21903:27;21918:11;21903:10;21935:2;21910;21903:6;:10::i;23445:1579::-;23611:7;23644:16;23663:4;:17;;23681:14;23663:33;;;;;;;;;;;;;;;;;;;;;23741:31;;;;;;23761:10;23741:31;;;;;;-1:-1:-1;;;;;23663:33:45;;;;-1:-1:-1;23663:33:45;;23741:19;;:31;;;;;;;;;;23663:33;23741:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;23741:31:45;23735:37;;;23710:125;;;;;-1:-1:-1;;;23710:125:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;23926:21;23950:9;-1:-1:-1;;;;;23950:19:45;;23978:4;23950:34;;;;;;;;;;;;;-1:-1:-1;;;;;23950:34:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;23950:34:45;;-1:-1:-1;23998:57:45;-1:-1:-1;;;;;23998:26:45;;24025:10;24045:4;24052:2;23998:26;:57::i;:::-;24137:53;24176:13;24137:9;-1:-1:-1;;;;;24137:19:45;;24165:4;24137:34;;;;;;;;;;;;;-1:-1:-1;;;;;24137:34:45;;;;;;;;;;;;;;;;;;;;;;;;;;:53;24132:58;;23445:1579;;24211:10;24231:13;24254:25;24282:4;:13;;24254:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;24319:134;24347:4;24365:14;24393:12;24419:2;24435:8;24319:14;:134::i;:::-;24305:148;;-1:-1:-1;24305:148:45;-1:-1:-1;24471:11:45;;;;24463:56;;;;;-1:-1:-1;;;24463:56:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;24530:18;24551:117;24614:4;:30;;24645:12;24614:44;;;;;;;;;;;;;;;;;;24551:45;3720:6;24551:24;24561:4;:13;;;24551:5;:9;;:24;;;;:::i;:117::-;24530:138;;24711:32;24740:2;24711:8;24720:14;24711:24;;;;;;;;;:32;24679:4;:13;;24693:14;24679:29;;;;;;;;;;;;;;;;;:64;;;;24783:68;24831:10;24783:30;24810:2;24783:8;24792:12;24783:22;;;;;;;;;:30;:34;;:68::i;:::-;24753:4;:13;;24767:12;24753:27;;;;;;;;;;;;;;;;;:98;;;;24862:60;24907:10;24919:2;24862:4;:17;;24880:12;24862:31;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;24862:31:45;;:60;:44;:60::i;:::-;24938:59;;;;;;;;;;;;;;;;;;;;;;;;;;;;24948:10;;24938:59;;;;;;;;;;-1:-1:-1;25015:2:45;;-1:-1:-1;;;23445:1579:45;;;;;;;;:::o;3782:44::-;3821:5;3782:44;:::o;4765:387::-;4911:7;4931:28;4965:143;5005:4;5023:11;5048:10;5072:4;:12;;;;;;;;;;-1:-1:-1;;;;;5072:12:45;-1:-1:-1;;;;;5072:24:45;;:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;5072:26:45;4965;:143::i;30951:1144::-;31154:12;;;;31207:17;;;31176:48;;;;;;;;;;;;;;;;;;;31117:7;;-1:-1:-1;;;;;31154:12:45;;31176:28;;:48;;31207:17;;31176:48;;;31207:17;31176:48;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;31176:48:45;;;;;;;;;;;;;;;;;;;;;;;31258:7;-1:-1:-1;;;;;31258:17:45;;31276:10;31258:29;;;;;;;;;;;;;-1:-1:-1;;;;;31258:29:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;31258:29:45;31243:44;;;31235:70;;;;;-1:-1:-1;;;31235:70:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;31336:12;:19;31323:10;:32;;;31315:60;;;;;-1:-1:-1;;;31315:60:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;31386:19;31408:7;-1:-1:-1;;;;;31408:19:45;;:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;31408:21:45;;-1:-1:-1;31441:10:45;;31470:128;31510:4;31528:11;31553:10;31408:21;31470:26;:128::i;:::-;31440:158;;;;31623:9;31617:2;:15;;31609:42;;;;;-1:-1:-1;;;31609:42:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;31690:106;31733:53;31740:45;3720:6;31740:24;31750:4;:13;;;31740:5;:9;;:24;;;;:::i;:45::-;31733:2;;:6;:53::i;:::-;31690:4;:13;;31704:10;31690:25;;;;;;;;;;;;;;;;;;:29;;:106;;;;:::i;:::-;31662:4;:13;;31676:10;31662:25;;;;;;;;;;;;;;;;;;:134;;;;31806:41;;;;;;31823:10;31806:41;;;;;;;;;;;;-1:-1:-1;;;;;31806:16:45;;;;;:41;;;;;;;;;;;31662:25;31806:16;:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;31857:53;31895:10;31907:2;31857:12;31870:10;31857:24;;;;;;;;;:53;31926:142;;;;;;;;;;;;;;;;;;;;;;;;;;;31958:10;;31926:142;;;;;;;;;;-1:-1:-1;32086:2:45;30951:1144;-1:-1:-1;;;;;;;;30951:1144:45:o;4069:46::-;4109:6;4069:46;:::o;3136:155:14:-;3194:7;3226:1;3221;:6;;3213:49;;;;;-1:-1:-1;;;3213:49:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;3279:5:14;;;3136:155::o;704:175:19:-;813:58;;;-1:-1:-1;;;;;813:58:19;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;836:23;813:58;;;786:86;;806:5;;786:19;:86::i;:::-;704:175;;;:::o;4256:134:45:-;4320:7;4346:37;4378:4;4346:31;:37::i;12817:519::-;12989:15;;13048:27;;12941:16;;12989:15;13035:40;;13014:118;;;;;-1:-1:-1;;;13014:118:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;13142:19;13178:9;13164:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;13164:24:45;;13142:46;;13203:9;13198:113;13222:9;13218:1;:13;13198:113;;;13260:40;13276:20;13297:1;13276:23;;;;;;;;;;;;;;13260:8;13269:1;13260:11;;;;;;;:40;13252:2;13255:1;13252:5;;;;;;;;;;;;;;;;;:48;13233:3;;13198:113;;;-1:-1:-1;13327:2:45;12817:519;-1:-1:-1;;;;12817:519:45:o;10538:1713::-;10674:9;;10631:7;;;;10712:81;10736:9;10732:1;:13;10712:81;;;10770:12;10776:2;10779:1;10776:5;;;;;;;;;;;;;;10770:1;:5;;:12;;;;:::i;:::-;10766:16;-1:-1:-1;10747:3:45;;10712:81;;;-1:-1:-1;10806:6:45;10802:45;;10835:1;10828:8;;;;;;10802:45;10857:13;10892:1;10857:13;10916:16;:1;10922:9;10916:5;:16::i;:::-;10903:29;;10948:9;10943:1007;4209:3;10963:1;:18;10943:1007;;;11015:1;11002:10;11030:378;11054:9;11050:1;:13;11030:378;;;11093:35;11107:20;11117:9;11107:2;11110:1;11107:5;;;;;;;:20;11093:9;:2;11100:1;11093:6;:9::i;:35::-;11088:40;-1:-1:-1;11065:3:45;;11030:378;;;-1:-1:-1;11429:1:45;;-1:-1:-1;11429:1:45;;11448:415;11634:211;11820:24;11841:2;11820:16;:9;11834:1;11820:13;:16::i;:24::-;11634:156;676:3:32;11634:95:45;11727:1;11634:63;:2;676:3:32;11634:31:45;:63::i;:156::-;:185;;:211::i;:::-;11448:143;11589:1;11448:119;11549:17;:2;11556:9;11549:6;:17::i;:::-;11448:79;676:3:32;11448:26:45;:2;11472:1;11448:23;:26::i;:415::-;11444:419;-1:-1:-1;11881:16:45;11444:419;11891:5;11881:9;:16::i;:::-;11877:63;;;11924:1;11917:8;;;;;;;;;;;11877:63;-1:-1:-1;10983:3:45;;10943:1007;;;-1:-1:-1;12215:29:45;;;-1:-1:-1;;;12215:29:45;;;;;;;;;;;;;;;;;;;;;;;;;;;885:203:19;1012:68;;;-1:-1:-1;;;;;1012:68:19;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1035:27;1012:68;;;985:96;;1005:5;;985:19;:96::i;2690:175:14:-;2748:7;2779:5;;;2802:6;;;;2794:46;;;;;-1:-1:-1;;;2794:46:14;;;;;;;;;;;;;;;;;;;;;;;;;;;22776:193:45;22881:7;22911:51;22938:23;22959:1;22938:16;:9;22952:1;22938:13;:16::i;:23::-;22911:22;:7;22923:9;3538:215:14;3596:7;3619:6;3615:20;;-1:-1:-1;3634:1:14;3627:8;;3615:20;3657:5;;;3661:1;3657;:5;:1;3680:5;;;;;:10;3672:56;;;;-1:-1:-1;;;3672:56:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4217:150;4275:7;4306:1;4302;:5;4294:44;;;;;-1:-1:-1;;;4294:44:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;4359:1;4355;:5;;;;;;;4217:150;-1:-1:-1;;;4217:150:14:o;975:160:36:-;1040:7;1067:1;1063;:5;1059:48;;;-1:-1:-1;1091:5:36;;;1084:12;;1059:48;-1:-1:-1;1123:5:36;;975:160::o;5432:163:14:-;5518:7;5553:12;5545:6;;;;5537:29;;;;-1:-1:-1;;;5537:29:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5583:5:14;;;5432:163::o;18057:888:45:-;18250:10;18262:13;18287:28;18318:4;:30;;18287:61;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;18358:19;18380:26;18384:8;18394:11;18380:3;:26::i;:::-;18358:48;;18454:2;:9;18437:14;:26;;;:54;;;;;18482:2;:9;18467:12;:24;;;18437:54;18416:125;;;;;-1:-1:-1;;;18416:125:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;18551:9;18563:59;18603:2;18606:14;18603:18;;;;;;;;;;;;;;;;18563:35;18570:11;18582:14;18570:27;;;;;;;;;;;;;;;;18563:2;:6;;:35;;;;:::i;:59::-;18551:71;;18632:9;18644:131;18662:18;18675:4;18662:12;:18::i;:::-;18694:14;18722:12;18748:1;18763:2;18644:4;:131::i;:::-;18632:143;;18790:30;18818:1;18790:23;18811:1;18790:2;18793:12;18790:16;;;;;;;;;:30;18785:35;;18838:41;3720:6;18838:20;18845:4;:12;;;18838:2;:6;;:20;;;;:::i;:41::-;18830:49;;18894:44;18912:11;18924:12;18912:25;;;;;;;;;;;;;;;;18894:13;18901:5;18894:2;:6;;:13;;;;:::i;:44::-;18889:49;;18057:888;;;;;;;;;;;;:::o;19589:473::-;19741:16;19787:11;19777:6;:21;;19769:60;;;;;-1:-1:-1;;;19769:60:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;19840:24;19881:8;:15;19867:30;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;19867:30:45;;19840:57;;19913:9;19908:124;19932:8;:15;19928:1;:19;19908:124;;;19981:40;20009:11;19981:23;19997:6;19981:8;19990:1;19981:11;;;;;;;:40;19968:7;19976:1;19968:10;;;;;;;;;;;;;;;;;:53;19949:3;;19908:124;;13598:147;13653:16;13688:50;13692:4;:13;;13688:50;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13707:4;:30;;13688:50;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:3;:50::i;5158:666::-;5334:7;5343;5362:10;5382:12;5404:16;5454:129;5495:4;5513:10;5537:11;5562;5454:27;:129::i;:::-;5431:152;;;;;;;;;;;;5651:17;5671:113;5781:2;5671:92;5720:4;:30;;5751:10;5720:42;;;;;;;;;;;;;;;;;;;;5671:31;:8;5697:4;5671:25;:31::i;:113::-;5803:2;;;;-1:-1:-1;5158:666:45;;-1:-1:-1;;;;;;;;5158:666:45:o;2967:751:19:-;3386:23;3412:69;3440:4;3412:69;;;;;;;;;;;;;;;;;3420:5;-1:-1:-1;;;;;3412:27:19;;;:69;;;;;:::i;:::-;3495:17;;3386:95;;-1:-1:-1;3495:21:19;3491:221;;3635:10;3624:30;;;;;;;;;;;;;;;-1:-1:-1;3624:30:19;3616:85;;;;-1:-1:-1;;;3616:85:19;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1747:1013:32;1881:16;;;;1950:12;;;;1845:7;;1881:16;2016:15;:20;-1:-1:-1;2012:742:32;;;2065:17;;;;2138:13;;2209:7;;;2205:499;;;2334:109;2366:55;2410:10;:2;2417;2410:6;:10::i;:::-;2366:39;2381:23;:15;2401:2;2381:19;:23::i;:::-;2366:10;:2;2373;2366:6;:10::i;:55::-;2334:2;;:6;:109::i;:::-;2307:136;;;;;;;;2205:499;2580:109;2612:55;2656:10;:2;2663;2656:6;:10::i;:::-;2612:39;2627:23;:15;2647:2;2627:19;:23::i;:::-;2612:10;:2;2619;2612:6;:10::i;:55::-;2580:2;;:6;:109::i;2012:742::-;2741:2;-1:-1:-1;2734:9:32;;-1:-1:-1;2734:9:32;654:115:36;716:4;760:1;740:16;751:1;754;740:10;:16::i;:::-;:21;;;654:115;-1:-1:-1;;;654:115:36:o;14983:1724:45:-;15197:9;;15158:7;;15237:30;;;;;;;;;15216:106;;;;;-1:-1:-1;;;15216:106:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;15370:9;15353:14;:26;;;:54;;;;;15398:9;15383:12;:24;;;15353:54;15332:123;;;;;-1:-1:-1;;;15332:123:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;15466:9;15478:18;15483:2;15487:8;15478:4;:18::i;:::-;15466:30;-1:-1:-1;15466:30:45;15506:9;;15561:23;:9;15575:8;15561:13;:23::i;:::-;15548:36;-1:-1:-1;15595:10:45;;15615:574;15639:9;15635:1;:13;15615:574;;;15678:14;15673:19;;:1;:19;15669:183;;;15717:1;15712:6;;15669:183;;;15748:12;15743:17;;:1;:17;15739:113;;15785:2;15788:1;15785:5;;;;;;;;;;;;;;15780:10;;15739:113;;;15829:8;;15739:113;15869:9;:1;15875:2;15869:5;:9::i;:::-;15865:13;-1:-1:-1;15896:31:45;15909:17;:2;15916:9;15909:6;:17::i;:::-;15896:8;:1;15902;15896:5;:8::i;:31::-;15892:35;;15615:574;15650:3;;15615:574;;;-1:-1:-1;16202:67:45;16251:17;:2;16258:9;16251:6;:17::i;:::-;16202:44;676:3:32;16202:8:45;:1;16208;16202:5;:8::i;:67::-;16198:71;-1:-1:-1;16279:9:45;16291:52;16297:45;16339:2;16297:37;:1;676:3:32;16297:5:45;:37::i;:45::-;16291:1;;:5;:52::i;:::-;16279:64;-1:-1:-1;16353:13:45;16388:1;16353:13;16435:216;4209:3;16455:1;:18;16435:216;;;16502:1;;-1:-1:-1;16502:1:45;;16521:43;16541:22;16561:1;16541:15;16554:1;16541:8;16502:1;16547;16541:5;:8::i;:22::-;16521:15;16534:1;16521:8;16527:1;;16521:5;:8::i;:43::-;16517:47;-1:-1:-1;16582:16:45;16517:47;16592:5;16582:9;:16::i;:::-;16578:63;;;-1:-1:-1;16625:1:45;-1:-1:-1;16618:8:45;;-1:-1:-1;;;;;;;;16618:8:45;16578:63;16475:3;;16435:216;;;-1:-1:-1;16660:40:45;;;-1:-1:-1;;;16660:40:45;;;;;;;;;;;;;;;;;;;;;;;;;;;6140:1830;6354:7;6375;6396;6529:19;6551:9;6555:4;6551:3;:9::i;:::-;6529:31;;6592:2;:9;6579:10;:22;;;6571:59;;;;;-1:-1:-1;;;6571:59:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;6641:52;;:::i;:::-;6696:46;;;;;;;;6728:1;6696:46;;;;6731:1;6696:46;;;;6734:1;6696:46;;;;6737:1;6696:46;;;;6740:1;6696:46;;;6641:101;;6765:18;6778:4;6765:12;:18::i;:::-;6752:10;;;:31;;;6800:20;;6805:2;;6800:4;:20::i;:::-;6793:27;;;6837:48;;6846:38;;6872:11;;6846:21;;:11;;:15;:21::i;:38::-;6837:4;;;:8;:48::i;:::-;6830:4;;;:55;6919:14;;:2;;:14;;;;;;;;;;;;;;;;6904:11;:29;;6896:68;;;;;-1:-1:-1;;;6896:68:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;6984:39;6990:1;:10;;;7002;7014:2;7018:1;:4;;;6984:5;:39::i;:::-;6975:6;;;:48;7077:9;;7034:26;;7063:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;7063:24:45;;7034:53;;7114:37;7127:4;:12;;;7141:2;:9;7114:12;:37::i;:::-;7098:13;;;:53;7166:9;7161:566;7185:2;:9;7181:1;:13;7161:566;;;7215:11;7229:2;7232:1;7229:5;;;;;;;;;;;;;;7215:19;;7459:257;7484:218;3720:6;7484:197;7667:1;:13;;;7512:10;7507:15;;:1;:15;7506:138;;7638:4;;7628;;;;7612:32;;7620:23;;:13;;:3;;:7;:13::i;:23::-;7612:3;;:7;:32::i;:::-;7506:138;;;7550:35;7578:1;:6;;;7550:23;7568:1;:4;;;7550:13;7558:1;:4;;;7550:3;:7;;:13;;;;:::i;7459:257::-;7444:9;7454:1;7444:12;;;;;;;;;;;;;;;;;:272;-1:-1:-1;7196:3:45;;7161:566;;;;7737:10;7750:95;7789:46;7795:1;:10;;;7807;7819:9;7830:1;:4;;;7789:5;:46::i;:::-;7750:9;7760:10;7750:21;;;;;;;;;:95;7737:108;;7860:57;7874:4;:30;;7905:10;7874:42;;;;;;;;;;;;;;;;;;;;7860:9;:2;7867:1;7860:6;:9::i;:57::-;7855:62;;7936:2;7940:1;:6;;;7948:2;7951:10;7948:14;;;;;;;;;;;;;;;;7928:35;;;;;;;;;;6140:1830;;;;;;;;:::o;3581:193:25:-;3684:12;3715:52;3737:6;3745:4;3751:1;3754:12;3715:21;:52::i;8851:1274:45:-;9027:9;;8988:7;;9054:22;;;;-1:-1:-1;9046:50:45;;;;;-1:-1:-1;;;9046:50:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;9119:1;9107:9;;9162:16;:1;9168:9;9162:5;:16::i;:::-;9149:29;;9194:9;9189:453;9213:9;9209:1;:13;9189:453;;;9252:10;9247:15;;:1;:15;9243:389;;9286:12;9292:2;9295:1;9292:5;;;;;;;;;;;;;;9286:1;:5;;:12;;;;:::i;:::-;9282:16;;9320:34;9333:20;9343:9;9333:2;9336:1;9333:5;;;;;;;:20;9320:8;:1;9326;9320:5;:8::i;:34::-;9316:38;;9243:389;9224:3;;9189:453;;;-1:-1:-1;9655:67:45;9704:17;:2;9711:9;9704:6;:17::i;:::-;9655:44;676:3:32;9655:8:45;:1;9661;9655:5;:8::i;:67::-;9651:71;-1:-1:-1;9733:9:45;9745:52;9751:45;9793:2;9751:37;:1;676:3:32;9751:5:45;:37::i;9745:52::-;9733:64;-1:-1:-1;9807:13:45;9842:1;9807:13;9853:216;4209:3;9873:1;:18;9853:216;;;9920:1;;-1:-1:-1;9920:1:45;;9939:43;9959:22;9979:1;9959:15;9972:1;9959:8;9920:1;9965;9959:5;:8::i;:22::-;9939:15;9952:1;9939:8;9945:1;;9939:5;:8::i;:43::-;9935:47;-1:-1:-1;10000:16:45;9935:47;10010:5;10000:9;:16::i;:::-;9996:63;;;-1:-1:-1;10043:1:45;-1:-1:-1;10036:8:45;;-1:-1:-1;;;;;;10036:8:45;9996:63;9893:3;;9853:216;;4608:523:25;4735:12;4792:5;4767:21;:30;;4759:81;;;;-1:-1:-1;;;4759:81:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4858:18;4869:6;4858:10;:18::i;:::-;4850:60;;;;;-1:-1:-1;;;4850:60:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;4981:12;4995:23;5022:6;-1:-1:-1;;;;;5022:11:25;5042:5;5050:4;5022:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4980:75;;;;5072:52;5090:7;5099:10;5111:12;5072:17;:52::i;:::-;5065:59;4608:523;-1:-1:-1;;;;;;;4608:523:25:o;726:413::-;1086:20;1124:8;;;726:413::o;7091:725::-;7206:12;7234:7;7230:580;;;-1:-1:-1;7264:10:25;7257:17;;7230:580;7375:17;;:21;7371:429;;7633:10;7627:17;7693:15;7680:10;7676:2;7672:19;7665:44;7582:145;7765:20;;-1:-1:-1;;;7765:20:25;;;;;;;;;;;;;;;;;7772:12;;7765:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;
Swarm Source
ipfs://060a6187b423dfd039618df2673efa5d77c62c64dfcfa6231e18e380d3d211e6
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.