Overview
POL Balance
0 POL
POL Value
$0.00More Info
Private Name Tags
ContractCreator
Loading...
Loading
Contract Name:
MetaSwapUtils
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/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../LPToken.sol"; import "../interfaces/ISwap.sol"; import "../MathUtils.sol"; import "../SwapUtils.sol"; /** * @title MetaSwapUtils library * @notice A library to be used within MetaSwap.sol. Contains functions responsible for custody and AMM functionalities. * * MetaSwap is a modified version of Swap that allows Swap's LP token to be utilized in pooling with other tokens. * As an example, if there is a Swap pool consisting of [DAI, USDC, USDT]. Then a MetaSwap pool can be created * with [sUSD, BaseSwapLPToken] to allow trades between either the LP token or the underlying tokens and sUSD. * * @dev Contracts relying on this library must initialize SwapUtils.Swap struct then use this library * for SwapUtils.Swap struct. Note that this library contains both functions called by users and admins. * Admin functions should be protected within contracts using this library. */ library MetaSwapUtils { using SafeERC20 for IERC20; using SafeMath for uint256; using MathUtils for uint256; using AmplificationUtils for SwapUtils.Swap; /*** EVENTS ***/ event TokenSwap( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); event TokenSwapUnderlying( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); event AddLiquidity( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event RemoveLiquidityOne( address indexed provider, uint256 lpTokenAmount, uint256 lpTokenSupply, uint256 boughtId, uint256 tokensBought ); event RemoveLiquidityImbalance( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event NewAdminFee(uint256 newAdminFee); event NewSwapFee(uint256 newSwapFee); event NewWithdrawFee(uint256 newWithdrawFee); struct MetaSwap { // Meta-Swap related parameters ISwap baseSwap; uint256 baseVirtualPrice; uint256 baseCacheLastUpdated; IERC20[] baseTokens; } // Struct storing variables used in calculations in the // calculateWithdrawOneTokenDY function to avoid stack too deep errors struct CalculateWithdrawOneTokenDYInfo { uint256 d0; uint256 d1; uint256 newY; uint256 feePerToken; uint256 preciseA; uint256 xpi; } // Struct storing variables used in calculation in removeLiquidityImbalance function // to avoid stack too deep error struct ManageLiquidityInfo { uint256 d0; uint256 d1; uint256 d2; LPToken lpToken; uint256 totalSupply; uint256 preciseA; uint256 baseVirtualPrice; uint256[] tokenPrecisionMultipliers; uint256[] newBalances; } struct SwapUnderlyingInfo { uint256 x; uint256 dx; uint256 dy; uint256[] tokenPrecisionMultipliers; uint256[] oldBalances; IERC20[] baseTokens; IERC20 tokenFrom; uint8 metaIndexFrom; IERC20 tokenTo; uint8 metaIndexTo; uint256 baseVirtualPrice; } struct CalculateSwapUnderlyingInfo { uint256 baseVirtualPrice; ISwap baseSwap; uint8 baseLPTokenIndex; uint8 baseTokensLength; uint8 metaIndexTo; uint256 x; uint256 dy; } // the denominator used to calculate admin and LP fees. For example, an // LP fee might be something like tradeAmount.mul(fee).div(FEE_DENOMINATOR) uint256 private constant FEE_DENOMINATOR = 10**10; // Cache expire time for the stored value of base Swap's virtual price uint256 public constant BASE_CACHE_EXPIRE_TIME = 10 minutes; uint256 public constant BASE_VIRTUAL_PRICE_PRECISION = 10**18; /*** VIEW & PURE FUNCTIONS ***/ /** * @notice Return the stored value of base Swap's virtual price. If * value was updated past BASE_CACHE_EXPIRE_TIME, then read it directly * from the base Swap contract. * @param metaSwapStorage MetaSwap struct to read from * @return base Swap's virtual price */ function _getBaseVirtualPrice(MetaSwap storage metaSwapStorage) internal view returns (uint256) { if ( block.timestamp > metaSwapStorage.baseCacheLastUpdated + BASE_CACHE_EXPIRE_TIME ) { return metaSwapStorage.baseSwap.getVirtualPrice(); } return metaSwapStorage.baseVirtualPrice; } function _getBaseSwapFee(ISwap baseSwap) internal view returns (uint256 swapFee) { (, , , , swapFee, , ) = baseSwap.swapStorage(); } /** * @notice Calculate how much the user would receive when withdrawing via single token * @param self Swap struct to read from * @param metaSwapStorage MetaSwap struct to read from * @param tokenAmount the amount to withdraw in the pool's precision * @param tokenIndex which token will be withdrawn * @return dy the amount of token user will receive */ function calculateWithdrawOneToken( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256 dy) { (dy, ) = _calculateWithdrawOneToken( self, tokenAmount, tokenIndex, _getBaseVirtualPrice(metaSwapStorage), self.lpToken.totalSupply() ); } function _calculateWithdrawOneToken( SwapUtils.Swap storage self, uint256 tokenAmount, uint8 tokenIndex, uint256 baseVirtualPrice, uint256 totalSupply ) internal view returns (uint256, uint256) { uint256 dy; uint256 dySwapFee; { uint256 currentY; uint256 newY; // Calculate how much to withdraw (dy, newY, currentY) = _calculateWithdrawOneTokenDY( self, tokenIndex, tokenAmount, baseVirtualPrice, totalSupply ); // Calculate the associated swap fee dySwapFee = currentY .sub(newY) .div(self.tokenPrecisionMultipliers[tokenIndex]) .sub(dy); } return (dy, dySwapFee); } /** * @notice Calculate the dy of withdrawing in one token * @param self Swap struct to read from * @param tokenIndex which token will be withdrawn * @param tokenAmount the amount to withdraw in the pools precision * @param baseVirtualPrice the virtual price of the base swap's LP token * @return the dy excluding swap fee, the new y after withdrawing one token, and current y */ function _calculateWithdrawOneTokenDY( SwapUtils.Swap storage self, uint8 tokenIndex, uint256 tokenAmount, uint256 baseVirtualPrice, uint256 totalSupply ) internal view returns ( uint256, uint256, uint256 ) { // Get the current D, then solve the stableswap invariant // y_i for D - tokenAmount uint256[] memory xp = _xp(self, baseVirtualPrice); require(tokenIndex < xp.length, "Token index out of range"); CalculateWithdrawOneTokenDYInfo memory v = CalculateWithdrawOneTokenDYInfo( 0, 0, 0, 0, self._getAPrecise(), 0 ); v.d0 = SwapUtils.getD(xp, v.preciseA); v.d1 = v.d0.sub(tokenAmount.mul(v.d0).div(totalSupply)); require(tokenAmount <= xp[tokenIndex], "Withdraw exceeds available"); v.newY = SwapUtils.getYD(v.preciseA, tokenIndex, xp, v.d1); uint256[] memory xpReduced = new uint256[](xp.length); v.feePerToken = SwapUtils._feePerToken(self.swapFee, xp.length); for (uint256 i = 0; i < xp.length; i++) { v.xpi = xp[i]; // if i == tokenIndex, dxExpected = xp[i] * d1 / d0 - newY // else dxExpected = xp[i] - (xp[i] * d1 / d0) // xpReduced[i] -= dxExpected * fee / FEE_DENOMINATOR xpReduced[i] = v.xpi.sub( ( (i == tokenIndex) ? v.xpi.mul(v.d1).div(v.d0).sub(v.newY) : v.xpi.sub(v.xpi.mul(v.d1).div(v.d0)) ).mul(v.feePerToken).div(FEE_DENOMINATOR) ); } uint256 dy = xpReduced[tokenIndex].sub( SwapUtils.getYD(v.preciseA, tokenIndex, xpReduced, v.d1) ); if (tokenIndex == xp.length.sub(1)) { dy = dy.mul(BASE_VIRTUAL_PRICE_PRECISION).div(baseVirtualPrice); v.newY = v.newY.mul(BASE_VIRTUAL_PRICE_PRECISION).div( baseVirtualPrice ); xp[tokenIndex] = xp[tokenIndex] .mul(BASE_VIRTUAL_PRICE_PRECISION) .div(baseVirtualPrice); } dy = dy.sub(1).div(self.tokenPrecisionMultipliers[tokenIndex]); return (dy, v.newY, xp[tokenIndex]); } /** * @notice Given a set of balances and precision multipliers, return the * precision-adjusted balances. The last element will also get scaled up by * the given baseVirtualPrice. * * @param balances an array of token balances, in their native precisions. * These should generally correspond with pooled tokens. * * @param precisionMultipliers an array of multipliers, corresponding to * the amounts in the balances array. When multiplied together they * should yield amounts at the pool's precision. * * @param baseVirtualPrice the base virtual price to scale the balance of the * base Swap's LP token. * * @return an array of amounts "scaled" to the pool's precision */ function _xp( uint256[] memory balances, uint256[] memory precisionMultipliers, uint256 baseVirtualPrice ) internal pure returns (uint256[] memory) { uint256[] memory xp = SwapUtils._xp(balances, precisionMultipliers); uint256 baseLPTokenIndex = balances.length.sub(1); xp[baseLPTokenIndex] = xp[baseLPTokenIndex].mul(baseVirtualPrice).div( BASE_VIRTUAL_PRICE_PRECISION ); return xp; } /** * @notice Return the precision-adjusted balances of all tokens in the pool * @param self Swap struct to read from * @return the pool balances "scaled" to the pool's precision, allowing * them to be more easily compared. */ function _xp(SwapUtils.Swap storage self, uint256 baseVirtualPrice) internal view returns (uint256[] memory) { return _xp( self.balances, self.tokenPrecisionMultipliers, baseVirtualPrice ); } /** * @notice Get the virtual price, to help calculate profit * @param self Swap struct to read from * @param metaSwapStorage MetaSwap struct to read from * @return the virtual price, scaled to precision of BASE_VIRTUAL_PRICE_PRECISION */ function getVirtualPrice( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage ) external view returns (uint256) { uint256 d = SwapUtils.getD( _xp( self.balances, self.tokenPrecisionMultipliers, _getBaseVirtualPrice(metaSwapStorage) ), self._getAPrecise() ); uint256 supply = self.lpToken.totalSupply(); if (supply != 0) { return d.mul(BASE_VIRTUAL_PRICE_PRECISION).div(supply); } return 0; } /** * @notice Externally calculates a swap between two tokens. The SwapUtils.Swap storage and * MetaSwap storage should be from the same MetaSwap contract. * @param self Swap struct to read from * @param metaSwapStorage MetaSwap struct from the same contract * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @return dy the number of tokens the user will get */ function calculateSwap( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256 dy) { (dy, ) = _calculateSwap( self, tokenIndexFrom, tokenIndexTo, dx, _getBaseVirtualPrice(metaSwapStorage) ); } /** * @notice Internally calculates a swap between two tokens. * * @dev The caller is expected to transfer the actual amounts (dx and dy) * using the token contracts. * * @param self Swap struct to read from * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @param baseVirtualPrice the virtual price of the base LP token * @return dy the number of tokens the user will get and dyFee the associated fee */ function _calculateSwap( SwapUtils.Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 baseVirtualPrice ) internal view returns (uint256 dy, uint256 dyFee) { uint256[] memory xp = _xp(self, baseVirtualPrice); require( tokenIndexFrom < xp.length && tokenIndexTo < xp.length, "Token index out of range" ); uint256 baseLPTokenIndex = xp.length.sub(1); uint256 x = dx.mul(self.tokenPrecisionMultipliers[tokenIndexFrom]); if (tokenIndexFrom == baseLPTokenIndex) { // When swapping from a base Swap token, scale up dx by its virtual price x = x.mul(baseVirtualPrice).div(BASE_VIRTUAL_PRICE_PRECISION); } x = x.add(xp[tokenIndexFrom]); uint256 y = SwapUtils.getY( self._getAPrecise(), tokenIndexFrom, tokenIndexTo, x, xp ); dy = xp[tokenIndexTo].sub(y).sub(1); if (tokenIndexTo == baseLPTokenIndex) { // When swapping to a base Swap token, scale down dy by its virtual price dy = dy.mul(BASE_VIRTUAL_PRICE_PRECISION).div(baseVirtualPrice); } dyFee = dy.mul(self.swapFee).div(FEE_DENOMINATOR); dy = dy.sub(dyFee); dy = dy.div(self.tokenPrecisionMultipliers[tokenIndexTo]); } /** * @notice Calculates the expected return amount from swapping between * the pooled tokens and the underlying tokens of the base Swap pool. * * @param self Swap struct to read from * @param metaSwapStorage MetaSwap struct from the same contract * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @return dy the number of tokens the user will get */ function calculateSwapUnderlying( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256) { CalculateSwapUnderlyingInfo memory v = CalculateSwapUnderlyingInfo( _getBaseVirtualPrice(metaSwapStorage), metaSwapStorage.baseSwap, 0, uint8(metaSwapStorage.baseTokens.length), 0, 0, 0 ); uint256[] memory xp = _xp(self, v.baseVirtualPrice); v.baseLPTokenIndex = uint8(xp.length.sub(1)); { uint8 maxRange = v.baseLPTokenIndex + v.baseTokensLength; require( tokenIndexFrom < maxRange && tokenIndexTo < maxRange, "Token index out of range" ); } if (tokenIndexFrom < v.baseLPTokenIndex) { // tokenFrom is from this pool v.x = xp[tokenIndexFrom].add( dx.mul(self.tokenPrecisionMultipliers[tokenIndexFrom]) ); } else { // tokenFrom is from the base pool tokenIndexFrom = tokenIndexFrom - v.baseLPTokenIndex; if (tokenIndexTo < v.baseLPTokenIndex) { uint256[] memory baseInputs = new uint256[](v.baseTokensLength); baseInputs[tokenIndexFrom] = dx; v.x = v .baseSwap .calculateTokenAmount(baseInputs, true) .mul(v.baseVirtualPrice) .div(BASE_VIRTUAL_PRICE_PRECISION); // when adding to the base pool,you pay approx 50% of the swap fee v.x = v .x .sub( v.x.mul(_getBaseSwapFee(metaSwapStorage.baseSwap)).div( FEE_DENOMINATOR.mul(2) ) ) .add(xp[v.baseLPTokenIndex]); } else { // both from and to are from the base pool return v.baseSwap.calculateSwap( tokenIndexFrom, tokenIndexTo - v.baseLPTokenIndex, dx ); } tokenIndexFrom = v.baseLPTokenIndex; } v.metaIndexTo = v.baseLPTokenIndex; if (tokenIndexTo < v.baseLPTokenIndex) { v.metaIndexTo = tokenIndexTo; } { uint256 y = SwapUtils.getY( self._getAPrecise(), tokenIndexFrom, v.metaIndexTo, v.x, xp ); v.dy = xp[v.metaIndexTo].sub(y).sub(1); uint256 dyFee = v.dy.mul(self.swapFee).div(FEE_DENOMINATOR); v.dy = v.dy.sub(dyFee); } if (tokenIndexTo < v.baseLPTokenIndex) { // tokenTo is from this pool v.dy = v.dy.div(self.tokenPrecisionMultipliers[v.metaIndexTo]); } else { // tokenTo is from the base pool v.dy = v.baseSwap.calculateRemoveLiquidityOneToken( v.dy.mul(BASE_VIRTUAL_PRICE_PRECISION).div(v.baseVirtualPrice), tokenIndexTo - v.baseLPTokenIndex ); } return v.dy; } /** * @notice A simple method to calculate prices from deposits or * withdrawals, excluding fees but including slippage. This is * helpful as an input into the various "min" parameters on calls * to fight front-running * * @dev This shouldn't be used outside frontends for user estimates. * * @param self Swap struct to read from * @param metaSwapStorage MetaSwap struct to read from * @param amounts an array of token amounts to deposit or withdrawal, * corresponding to pooledTokens. The amount should be in each * pooled token's native precision. If a token charges a fee on transfers, * use the amount that gets transferred after the fee. * @param deposit whether this is a deposit or a withdrawal * @return if deposit was true, total amount of lp token that will be minted and if * deposit was false, total amount of lp token that will be burned */ function calculateTokenAmount( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint256[] calldata amounts, bool deposit ) external view returns (uint256) { uint256 a = self._getAPrecise(); uint256 d0; uint256 d1; { uint256 baseVirtualPrice = _getBaseVirtualPrice(metaSwapStorage); uint256[] memory balances1 = self.balances; uint256[] memory tokenPrecisionMultipliers = self .tokenPrecisionMultipliers; uint256 numTokens = balances1.length; d0 = SwapUtils.getD( _xp(balances1, tokenPrecisionMultipliers, baseVirtualPrice), a ); for (uint256 i = 0; i < numTokens; i++) { if (deposit) { balances1[i] = balances1[i].add(amounts[i]); } else { balances1[i] = balances1[i].sub( amounts[i], "Cannot withdraw more than available" ); } } d1 = SwapUtils.getD( _xp(balances1, tokenPrecisionMultipliers, baseVirtualPrice), a ); } uint256 totalSupply = self.lpToken.totalSupply(); if (deposit) { return d1.sub(d0).mul(totalSupply).div(d0); } else { return d0.sub(d1).mul(totalSupply).div(d0); } } /*** STATE MODIFYING FUNCTIONS ***/ /** * @notice swap two tokens in the pool * @param self Swap struct to read from and write to * @param metaSwapStorage MetaSwap struct to read from and write to * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell * @param minDy the min amount the user would like to receive, or revert. * @return amount of token user received on swap */ function swap( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy ) external returns (uint256) { { uint256 pooledTokensLength = self.pooledTokens.length; require( tokenIndexFrom < pooledTokensLength && tokenIndexTo < pooledTokensLength, "Token index is out of range" ); } uint256 transferredDx; { IERC20 tokenFrom = self.pooledTokens[tokenIndexFrom]; require( dx <= tokenFrom.balanceOf(msg.sender), "Cannot swap more than you own" ); { // Transfer tokens first to see if a fee was charged on transfer uint256 beforeBalance = tokenFrom.balanceOf(address(this)); tokenFrom.safeTransferFrom(msg.sender, address(this), dx); // Use the actual transferred amount for AMM math transferredDx = tokenFrom.balanceOf(address(this)).sub( beforeBalance ); } } (uint256 dy, uint256 dyFee) = _calculateSwap( self, tokenIndexFrom, tokenIndexTo, transferredDx, _updateBaseVirtualPrice(metaSwapStorage) ); require(dy >= minDy, "Swap didn't result in min tokens"); uint256 dyAdminFee = dyFee.mul(self.adminFee).div(FEE_DENOMINATOR).div( self.tokenPrecisionMultipliers[tokenIndexTo] ); self.balances[tokenIndexFrom] = self.balances[tokenIndexFrom].add( transferredDx ); self.balances[tokenIndexTo] = self.balances[tokenIndexTo].sub(dy).sub( dyAdminFee ); self.pooledTokens[tokenIndexTo].safeTransfer(msg.sender, dy); emit TokenSwap( msg.sender, transferredDx, dy, tokenIndexFrom, tokenIndexTo ); return dy; } /** * @notice Swaps with the underlying tokens of the base Swap pool. For this function, * the token indices are flattened out so that underlying tokens are represented * in the indices. * @dev Since this calls multiple external functions during the execution, * it is recommended to protect any function that depends on this with reentrancy guards. * @param self Swap struct to read from and write to * @param metaSwapStorage MetaSwap struct to read from and write to * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell * @param minDy the min amount the user would like to receive, or revert. * @return amount of token user received on swap */ function swapUnderlying( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy ) external returns (uint256) { SwapUnderlyingInfo memory v = SwapUnderlyingInfo( 0, 0, 0, self.tokenPrecisionMultipliers, self.balances, metaSwapStorage.baseTokens, IERC20(address(0)), 0, IERC20(address(0)), 0, _updateBaseVirtualPrice(metaSwapStorage) ); uint8 baseLPTokenIndex = uint8(v.oldBalances.length.sub(1)); { uint8 maxRange = uint8(baseLPTokenIndex + v.baseTokens.length); require( tokenIndexFrom < maxRange && tokenIndexTo < maxRange, "Token index out of range" ); } ISwap baseSwap = metaSwapStorage.baseSwap; // Find the address of the token swapping from and the index in MetaSwap's token list if (tokenIndexFrom < baseLPTokenIndex) { v.tokenFrom = self.pooledTokens[tokenIndexFrom]; v.metaIndexFrom = tokenIndexFrom; } else { v.tokenFrom = v.baseTokens[tokenIndexFrom - baseLPTokenIndex]; v.metaIndexFrom = baseLPTokenIndex; } // Find the address of the token swapping to and the index in MetaSwap's token list if (tokenIndexTo < baseLPTokenIndex) { v.tokenTo = self.pooledTokens[tokenIndexTo]; v.metaIndexTo = tokenIndexTo; } else { v.tokenTo = v.baseTokens[tokenIndexTo - baseLPTokenIndex]; v.metaIndexTo = baseLPTokenIndex; } // Check for possible fee on transfer v.dx = v.tokenFrom.balanceOf(address(this)); v.tokenFrom.safeTransferFrom(msg.sender, address(this), dx); v.dx = v.tokenFrom.balanceOf(address(this)).sub(v.dx); // update dx in case of fee on transfer if ( tokenIndexFrom < baseLPTokenIndex || tokenIndexTo < baseLPTokenIndex ) { // Either one of the tokens belongs to the MetaSwap tokens list uint256[] memory xp = _xp( v.oldBalances, v.tokenPrecisionMultipliers, v.baseVirtualPrice ); if (tokenIndexFrom < baseLPTokenIndex) { // Swapping from a MetaSwap token v.x = xp[tokenIndexFrom].add( dx.mul(v.tokenPrecisionMultipliers[tokenIndexFrom]) ); } else { // Swapping from one of the tokens hosted in the base Swap // This case requires adding the underlying token to the base Swap, then // using the base LP token to swap to the desired token uint256[] memory baseAmounts = new uint256[]( v.baseTokens.length ); baseAmounts[tokenIndexFrom - baseLPTokenIndex] = v.dx; // Add liquidity to the base Swap contract and receive base LP token v.dx = baseSwap.addLiquidity(baseAmounts, 0, block.timestamp); // Calculate the value of total amount of baseLPToken we end up with v.x = v .dx .mul(v.baseVirtualPrice) .div(BASE_VIRTUAL_PRICE_PRECISION) .add(xp[baseLPTokenIndex]); } // Calculate how much to withdraw in MetaSwap level and the the associated swap fee uint256 dyFee; { uint256 y = SwapUtils.getY( self._getAPrecise(), v.metaIndexFrom, v.metaIndexTo, v.x, xp ); v.dy = xp[v.metaIndexTo].sub(y).sub(1); if (tokenIndexTo >= baseLPTokenIndex) { // When swapping to a base Swap token, scale down dy by its virtual price v.dy = v.dy.mul(BASE_VIRTUAL_PRICE_PRECISION).div( v.baseVirtualPrice ); } dyFee = v.dy.mul(self.swapFee).div(FEE_DENOMINATOR); v.dy = v.dy.sub(dyFee).div( v.tokenPrecisionMultipliers[v.metaIndexTo] ); } // Update the balances array according to the calculated input and output amount { uint256 dyAdminFee = dyFee.mul(self.adminFee).div( FEE_DENOMINATOR ); dyAdminFee = dyAdminFee.div( v.tokenPrecisionMultipliers[v.metaIndexTo] ); self.balances[v.metaIndexFrom] = v .oldBalances[v.metaIndexFrom] .add(v.dx); self.balances[v.metaIndexTo] = v .oldBalances[v.metaIndexTo] .sub(v.dy) .sub(dyAdminFee); } if (tokenIndexTo >= baseLPTokenIndex) { // When swapping to a token that belongs to the base Swap, burn the LP token // and withdraw the desired token from the base pool uint256 oldBalance = v.tokenTo.balanceOf(address(this)); baseSwap.removeLiquidityOneToken( v.dy, tokenIndexTo - baseLPTokenIndex, 0, block.timestamp ); v.dy = v.tokenTo.balanceOf(address(this)) - oldBalance; } // Check the amount of token to send meets minDy require(v.dy >= minDy, "Swap didn't result in min tokens"); } else { // Both tokens are from the base Swap pool // Do a swap through the base Swap v.dy = v.tokenTo.balanceOf(address(this)); baseSwap.swap( tokenIndexFrom - baseLPTokenIndex, tokenIndexTo - baseLPTokenIndex, v.dx, minDy, block.timestamp ); v.dy = v.tokenTo.balanceOf(address(this)).sub(v.dy); } // Send the desired token to the caller v.tokenTo.safeTransfer(msg.sender, v.dy); emit TokenSwapUnderlying( msg.sender, dx, v.dy, tokenIndexFrom, tokenIndexTo ); return v.dy; } /** * @notice Add liquidity to the pool * @param self Swap struct to read from and write to * @param metaSwapStorage MetaSwap struct to read from and write to * @param amounts the amounts of each token to add, in their native precision * @param minToMint the minimum LP tokens adding this amount of liquidity * should mint, otherwise revert. Handy for front-running mitigation * allowed addresses. If the pool is not in the guarded launch phase, this parameter will be ignored. * @return amount of LP token user received */ function addLiquidity( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint256[] memory amounts, uint256 minToMint ) external returns (uint256) { IERC20[] memory pooledTokens = self.pooledTokens; require( amounts.length == pooledTokens.length, "Amounts must match pooled tokens" ); uint256[] memory fees = new uint256[](pooledTokens.length); // current state ManageLiquidityInfo memory v = ManageLiquidityInfo( 0, 0, 0, self.lpToken, 0, self._getAPrecise(), _updateBaseVirtualPrice(metaSwapStorage), self.tokenPrecisionMultipliers, self.balances ); v.totalSupply = v.lpToken.totalSupply(); if (v.totalSupply != 0) { v.d0 = SwapUtils.getD( _xp( v.newBalances, v.tokenPrecisionMultipliers, v.baseVirtualPrice ), v.preciseA ); } for (uint256 i = 0; i < pooledTokens.length; i++) { require( v.totalSupply != 0 || amounts[i] > 0, "Must supply all tokens in pool" ); // Transfer tokens first to see if a fee was charged on transfer if (amounts[i] != 0) { uint256 beforeBalance = pooledTokens[i].balanceOf( address(this) ); pooledTokens[i].safeTransferFrom( msg.sender, address(this), amounts[i] ); // Update the amounts[] with actual transfer amount amounts[i] = pooledTokens[i].balanceOf(address(this)).sub( beforeBalance ); } v.newBalances[i] = v.newBalances[i].add(amounts[i]); } // invariant after change v.d1 = SwapUtils.getD( _xp(v.newBalances, v.tokenPrecisionMultipliers, v.baseVirtualPrice), v.preciseA ); require(v.d1 > v.d0, "D should increase"); // updated to reflect fees and calculate the user's LP tokens v.d2 = v.d1; uint256 toMint; if (v.totalSupply != 0) { uint256 feePerToken = SwapUtils._feePerToken( self.swapFee, pooledTokens.length ); for (uint256 i = 0; i < pooledTokens.length; i++) { uint256 idealBalance = v.d1.mul(self.balances[i]).div(v.d0); fees[i] = feePerToken .mul(idealBalance.difference(v.newBalances[i])) .div(FEE_DENOMINATOR); self.balances[i] = v.newBalances[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); v.newBalances[i] = v.newBalances[i].sub(fees[i]); } v.d2 = SwapUtils.getD( _xp( v.newBalances, v.tokenPrecisionMultipliers, v.baseVirtualPrice ), v.preciseA ); toMint = v.d2.sub(v.d0).mul(v.totalSupply).div(v.d0); } else { // the initial depositor doesn't pay fees self.balances = v.newBalances; toMint = v.d1; } require(toMint >= minToMint, "Couldn't mint min requested"); // mint the user's LP tokens self.lpToken.mint(msg.sender, toMint); emit AddLiquidity( msg.sender, amounts, fees, v.d1, v.totalSupply.add(toMint) ); return toMint; } /** * @notice Remove liquidity from the pool all in one token. * @param self Swap struct to read from and write to * @param metaSwapStorage MetaSwap struct to read from and write to * @param tokenAmount the amount of the lp tokens to burn * @param tokenIndex the index of the token you want to receive * @param minAmount the minimum amount to withdraw, otherwise revert * @return amount chosen token that user received */ function removeLiquidityOneToken( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount ) external returns (uint256) { LPToken lpToken = self.lpToken; uint256 totalSupply = lpToken.totalSupply(); uint256 numTokens = self.pooledTokens.length; require(tokenAmount <= lpToken.balanceOf(msg.sender), ">LP.balanceOf"); require(tokenIndex < numTokens, "Token not found"); uint256 dyFee; uint256 dy; (dy, dyFee) = _calculateWithdrawOneToken( self, tokenAmount, tokenIndex, _updateBaseVirtualPrice(metaSwapStorage), totalSupply ); require(dy >= minAmount, "dy < minAmount"); // Update balances array self.balances[tokenIndex] = self.balances[tokenIndex].sub( dy.add(dyFee.mul(self.adminFee).div(FEE_DENOMINATOR)) ); // Burn the associated LP token from the caller and send the desired token lpToken.burnFrom(msg.sender, tokenAmount); self.pooledTokens[tokenIndex].safeTransfer(msg.sender, dy); emit RemoveLiquidityOne( msg.sender, tokenAmount, totalSupply, tokenIndex, dy ); return dy; } /** * @notice Remove liquidity from the pool, weighted differently than the * pool's current balances. * * @param self Swap struct to read from and write to * @param metaSwapStorage MetaSwap struct to read from and write to * @param amounts how much of each token to withdraw * @param maxBurnAmount the max LP token provider is willing to pay to * remove liquidity. Useful as a front-running mitigation. * @return actual amount of LP tokens burned in the withdrawal */ function removeLiquidityImbalance( SwapUtils.Swap storage self, MetaSwap storage metaSwapStorage, uint256[] memory amounts, uint256 maxBurnAmount ) public returns (uint256) { // Using this struct to avoid stack too deep error ManageLiquidityInfo memory v = ManageLiquidityInfo( 0, 0, 0, self.lpToken, 0, self._getAPrecise(), _updateBaseVirtualPrice(metaSwapStorage), self.tokenPrecisionMultipliers, self.balances ); v.totalSupply = v.lpToken.totalSupply(); require( amounts.length == v.newBalances.length, "Amounts should match pool tokens" ); require(maxBurnAmount != 0, "Must burn more than 0"); uint256 feePerToken = SwapUtils._feePerToken( self.swapFee, v.newBalances.length ); // Calculate how much LPToken should be burned uint256[] memory fees = new uint256[](v.newBalances.length); { uint256[] memory balances1 = new uint256[](v.newBalances.length); v.d0 = SwapUtils.getD( _xp( v.newBalances, v.tokenPrecisionMultipliers, v.baseVirtualPrice ), v.preciseA ); for (uint256 i = 0; i < v.newBalances.length; i++) { balances1[i] = v.newBalances[i].sub( amounts[i], "Cannot withdraw more than available" ); } v.d1 = SwapUtils.getD( _xp(balances1, v.tokenPrecisionMultipliers, v.baseVirtualPrice), v.preciseA ); for (uint256 i = 0; i < v.newBalances.length; i++) { uint256 idealBalance = v.d1.mul(v.newBalances[i]).div(v.d0); uint256 difference = idealBalance.difference(balances1[i]); fees[i] = feePerToken.mul(difference).div(FEE_DENOMINATOR); self.balances[i] = balances1[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); balances1[i] = balances1[i].sub(fees[i]); } v.d2 = SwapUtils.getD( _xp(balances1, v.tokenPrecisionMultipliers, v.baseVirtualPrice), v.preciseA ); } uint256 tokenAmount = v.d0.sub(v.d2).mul(v.totalSupply).div(v.d0); require(tokenAmount != 0, "Burnt amount cannot be zero"); // Scale up by withdraw fee tokenAmount = tokenAmount.add(1); // Check for max burn amount require(tokenAmount <= maxBurnAmount, "tokenAmount > maxBurnAmount"); // Burn the calculated amount of LPToken from the caller and send the desired tokens v.lpToken.burnFrom(msg.sender, tokenAmount); for (uint256 i = 0; i < v.newBalances.length; i++) { self.pooledTokens[i].safeTransfer(msg.sender, amounts[i]); } emit RemoveLiquidityImbalance( msg.sender, amounts, fees, v.d1, v.totalSupply.sub(tokenAmount) ); return tokenAmount; } /** * @notice Determines if the stored value of base Swap's virtual price is expired. * If the last update was past the BASE_CACHE_EXPIRE_TIME, then update the stored value. * * @param metaSwapStorage MetaSwap struct to read from and write to * @return base Swap's virtual price */ function _updateBaseVirtualPrice(MetaSwap storage metaSwapStorage) internal returns (uint256) { if ( block.timestamp > metaSwapStorage.baseCacheLastUpdated + BASE_CACHE_EXPIRE_TIME ) { // When the cache is expired, update it uint256 baseVirtualPrice = ISwap(metaSwapStorage.baseSwap) .getVirtualPrice(); metaSwapStorage.baseVirtualPrice = baseVirtualPrice; metaSwapStorage.baseCacheLastUpdated = block.timestamp; return baseVirtualPrice; } else { return metaSwapStorage.baseVirtualPrice; } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../utils/ContextUpgradeable.sol"; import "../proxy/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ function __Ownable_init() internal initializer { __Context_init_unchained(); __Ownable_init_unchained(); } function __Ownable_init_unchained() internal initializer { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } uint256[49] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMathUpgradeable { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a % b; } }
// SPDX-License-Identifier: MIT // solhint-disable-next-line compiler-version pragma solidity >=0.4.24 <0.8.0; import "../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Modifier to protect an initializer function from being invoked twice. */ modifier initializer() { require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized"); bool isTopLevelCall = !_initializing; if (isTopLevelCall) { _initializing = true; _initialized = true; } _; if (isTopLevelCall) { _initializing = false; } } /// @dev Returns true if and only if the function is running in the constructor function _isConstructor() private view returns (bool) { return !AddressUpgradeable.isContract(address(this)); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../../utils/ContextUpgradeable.sol"; import "./ERC20Upgradeable.sol"; import "../../proxy/Initializable.sol"; /** * @dev Extension of {ERC20} that allows token holders to destroy both their own * tokens and those that they have an allowance for, in a way that can be * recognized off-chain (via event analysis). */ abstract contract ERC20BurnableUpgradeable is Initializable, ContextUpgradeable, ERC20Upgradeable { function __ERC20Burnable_init() internal initializer { __Context_init_unchained(); __ERC20Burnable_init_unchained(); } function __ERC20Burnable_init_unchained() internal initializer { } using SafeMathUpgradeable for uint256; /** * @dev Destroys `amount` tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 amount) public virtual { _burn(_msgSender(), amount); } /** * @dev Destroys `amount` tokens from `account`, deducting from the caller's * allowance. * * See {ERC20-_burn} and {ERC20-allowance}. * * Requirements: * * - the caller must have allowance for ``accounts``'s tokens of at least * `amount`. */ function burnFrom(address account, uint256 amount) public virtual { uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance"); _approve(account, _msgSender(), decreasedAllowance); _burn(account, amount); } uint256[50] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../../utils/ContextUpgradeable.sol"; import "./IERC20Upgradeable.sol"; import "../../math/SafeMathUpgradeable.sol"; import "../../proxy/Initializable.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable { using SafeMathUpgradeable for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ function __ERC20_init(string memory name_, string memory symbol_) internal initializer { __Context_init_unchained(); __ERC20_init_unchained(name_, symbol_); } function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer { _name = name_; _symbol = symbol_; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() public view virtual returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal virtual { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } uint256[44] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20Upgradeable { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../proxy/Initializable.sol"; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal initializer { __Context_init_unchained(); } function __Context_init_unchained() internal initializer { } function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } uint256[50] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a % b; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../../utils/Context.sol"; import "./IERC20.sol"; import "../../math/SafeMath.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name_, string memory symbol_) public { _name = name_; _symbol = symbol_; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() public view virtual returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal virtual { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "./IERC20.sol"; import "../../math/SafeMath.sol"; import "../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "./SwapUtils.sol"; /** * @title AmplificationUtils library * @notice A library to calculate and ramp the A parameter of a given `SwapUtils.Swap` struct. * This library assumes the struct is fully validated. */ library AmplificationUtils { using SafeMath for uint256; event RampA( uint256 oldA, uint256 newA, uint256 initialTime, uint256 futureTime ); event StopRampA(uint256 currentA, uint256 time); // Constant values used in ramping A calculations uint256 public constant A_PRECISION = 100; uint256 public constant MAX_A = 10**6; uint256 private constant MAX_A_CHANGE = 2; uint256 private constant MIN_RAMP_TIME = 14 days; /** * @notice Return A, the amplification coefficient * n * (n - 1) * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter */ function getA(SwapUtils.Swap storage self) external view returns (uint256) { return _getAPrecise(self).div(A_PRECISION); } /** * @notice Return A in its raw precision * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter in its raw precision form */ function getAPrecise(SwapUtils.Swap storage self) external view returns (uint256) { return _getAPrecise(self); } /** * @notice Return A in its raw precision * @dev See the StableSwap paper for details * @param self Swap struct to read from * @return A parameter in its raw precision form */ function _getAPrecise(SwapUtils.Swap storage self) internal view returns (uint256) { uint256 t1 = self.futureATime; // time when ramp is finished uint256 a1 = self.futureA; // final A value when ramp is finished if (block.timestamp < t1) { uint256 t0 = self.initialATime; // time when ramp is started uint256 a0 = self.initialA; // initial A value when ramp is started if (a1 > a0) { // a0 + (a1 - a0) * (block.timestamp - t0) / (t1 - t0) return a0.add( a1.sub(a0).mul(block.timestamp.sub(t0)).div(t1.sub(t0)) ); } else { // a0 - (a0 - a1) * (block.timestamp - t0) / (t1 - t0) return a0.sub( a0.sub(a1).mul(block.timestamp.sub(t0)).div(t1.sub(t0)) ); } } else { return a1; } } /** * @notice Start ramping up or down A parameter towards given futureA_ and futureTime_ * Checks if the change is too rapid, and commits the new A value only when it falls under * the limit range. * @param self Swap struct to update * @param futureA_ the new A to ramp towards * @param futureTime_ timestamp when the new A should be reached */ function rampA( SwapUtils.Swap storage self, uint256 futureA_, uint256 futureTime_ ) external { require( block.timestamp >= self.initialATime.add(1 days), "Wait 1 day before starting ramp" ); require( futureTime_ >= block.timestamp.add(MIN_RAMP_TIME), "Insufficient ramp time" ); require( futureA_ > 0 && futureA_ < MAX_A, "futureA_ must be > 0 and < MAX_A" ); uint256 initialAPrecise = _getAPrecise(self); uint256 futureAPrecise = futureA_.mul(A_PRECISION); if (futureAPrecise < initialAPrecise) { require( futureAPrecise.mul(MAX_A_CHANGE) >= initialAPrecise, "futureA_ is too small" ); } else { require( futureAPrecise <= initialAPrecise.mul(MAX_A_CHANGE), "futureA_ is too large" ); } self.initialA = initialAPrecise; self.futureA = futureAPrecise; self.initialATime = block.timestamp; self.futureATime = futureTime_; emit RampA( initialAPrecise, futureAPrecise, block.timestamp, futureTime_ ); } /** * @notice Stops ramping A immediately. Once this function is called, rampA() * cannot be called for another 24 hours * @param self Swap struct to update */ function stopRampA(SwapUtils.Swap storage self) external { require(self.futureATime > block.timestamp, "Ramp is already stopped"); uint256 currentA = _getAPrecise(self); self.initialA = currentA; self.futureA = currentA; self.initialATime = block.timestamp; self.futureATime = block.timestamp; emit StopRampA(currentA, block.timestamp); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20BurnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "./interfaces/ISwap.sol"; /** * @title Liquidity Provider Token * @notice This token is an ERC20 detailed token with added capability to be minted by the owner. * It is used to represent user's shares when providing liquidity to swap contracts. * @dev Only Swap contracts should initialize and own LPToken contracts. */ contract LPToken is ERC20BurnableUpgradeable, OwnableUpgradeable { using SafeMathUpgradeable for uint256; /** * @notice Initializes this LPToken contract with the given name and symbol * @dev The caller of this function will become the owner. A Swap contract should call this * in its initializer function. * @param name name of this token * @param symbol symbol of this token */ function initialize(string memory name, string memory symbol) external initializer returns (bool) { __Context_init_unchained(); __ERC20_init_unchained(name, symbol); __Ownable_init_unchained(); return true; } /** * @notice Mints the given amount of LPToken to the recipient. * @dev only owner can call this mint function * @param recipient address of account to receive the tokens * @param amount amount of tokens to mint */ function mint(address recipient, uint256 amount) external onlyOwner { require(amount != 0, "LPToken: cannot mint 0"); _mint(recipient, amount); } /** * @dev Overrides ERC20._beforeTokenTransfer() which get called on every transfers including * minting and burning. This ensures that Swap.updateUserWithdrawFees are called everytime. * This assumes the owner is set to a Swap contract's address. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual override(ERC20Upgradeable) { super._beforeTokenTransfer(from, to, amount); require(to != address(this), "LPToken: cannot send to itself"); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; /** * @title MathUtils library * @notice A library to be used in conjunction with SafeMath. Contains functions for calculating * differences between two uint256. */ library MathUtils { /** * @notice Compares a and b and returns true if the difference between a and b * is less than 1 or equal to each other. * @param a uint256 to compare with * @param b uint256 to compare with * @return True if the difference between a and b is less than 1 or equal, * otherwise return false */ function within1(uint256 a, uint256 b) internal pure returns (bool) { return (difference(a, b) <= 1); } /** * @notice Calculates absolute difference between a and b * @param a uint256 to compare with * @param b uint256 to compare with * @return Difference between a and b */ function difference(uint256 a, uint256 b) internal pure returns (uint256) { if (a > b) { return a - b; } return b - a; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "./AmplificationUtils.sol"; import "./LPToken.sol"; import "./MathUtils.sol"; /** * @title SwapUtils library * @notice A library to be used within Swap.sol. Contains functions responsible for custody and AMM functionalities. * @dev Contracts relying on this library must initialize SwapUtils.Swap struct then use this library * for SwapUtils.Swap struct. Note that this library contains both functions called by users and admins. * Admin functions should be protected within contracts using this library. */ library SwapUtils { using SafeERC20 for IERC20; using SafeMath for uint256; using MathUtils for uint256; /*** EVENTS ***/ event TokenSwap( address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId ); event AddLiquidity( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event RemoveLiquidity( address indexed provider, uint256[] tokenAmounts, uint256 lpTokenSupply ); event RemoveLiquidityOne( address indexed provider, uint256 lpTokenAmount, uint256 lpTokenSupply, uint256 boughtId, uint256 tokensBought ); event RemoveLiquidityImbalance( address indexed provider, uint256[] tokenAmounts, uint256[] fees, uint256 invariant, uint256 lpTokenSupply ); event NewAdminFee(uint256 newAdminFee); event NewSwapFee(uint256 newSwapFee); struct Swap { // variables around the ramp management of A, // the amplification coefficient * n * (n - 1) // see https://www.curve.fi/stableswap-paper.pdf for details uint256 initialA; uint256 futureA; uint256 initialATime; uint256 futureATime; // fee calculation uint256 swapFee; uint256 adminFee; LPToken lpToken; // contract references for all tokens being pooled IERC20[] pooledTokens; // multipliers for each pooled token's precision to get to POOL_PRECISION_DECIMALS // for example, TBTC has 18 decimals, so the multiplier should be 1. WBTC // has 8, so the multiplier should be 10 ** 18 / 10 ** 8 => 10 ** 10 uint256[] tokenPrecisionMultipliers; // the pool balance of each token, in the token's precision // the contract's actual token balance might differ uint256[] balances; } // Struct storing variables used in calculations in the // calculateWithdrawOneTokenDY function to avoid stack too deep errors struct CalculateWithdrawOneTokenDYInfo { uint256 d0; uint256 d1; uint256 newY; uint256 feePerToken; uint256 preciseA; } // Struct storing variables used in calculations in the // {add,remove}Liquidity functions to avoid stack too deep errors struct ManageLiquidityInfo { uint256 d0; uint256 d1; uint256 d2; uint256 preciseA; LPToken lpToken; uint256 totalSupply; uint256[] balances; uint256[] multipliers; } // the precision all pools tokens will be converted to uint8 public constant POOL_PRECISION_DECIMALS = 18; // the denominator used to calculate admin and LP fees. For example, an // LP fee might be something like tradeAmount.mul(fee).div(FEE_DENOMINATOR) uint256 private constant FEE_DENOMINATOR = 10**10; // Max swap fee is 1% or 100bps of each swap uint256 public constant MAX_SWAP_FEE = 10**8; // Max adminFee is 100% of the swapFee // adminFee does not add additional fee on top of swapFee // Instead it takes a certain % of the swapFee. Therefore it has no impact on the // users but only on the earnings of LPs uint256 public constant MAX_ADMIN_FEE = 10**10; // Constant value used as max loop limit uint256 private constant MAX_LOOP_LIMIT = 256; /*** VIEW & PURE FUNCTIONS ***/ function _getAPrecise(Swap storage self) internal view returns (uint256) { return AmplificationUtils._getAPrecise(self); } /** * @notice Calculate the dy, the amount of selected token that user receives and * the fee of withdrawing in one token * @param tokenAmount the amount to withdraw in the pool's precision * @param tokenIndex which token will be withdrawn * @param self Swap struct to read from * @return the amount of token user will receive */ function calculateWithdrawOneToken( Swap storage self, uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256) { (uint256 availableTokenAmount, ) = _calculateWithdrawOneToken( self, tokenAmount, tokenIndex, self.lpToken.totalSupply() ); return availableTokenAmount; } function _calculateWithdrawOneToken( Swap storage self, uint256 tokenAmount, uint8 tokenIndex, uint256 totalSupply ) internal view returns (uint256, uint256) { uint256 dy; uint256 newY; uint256 currentY; (dy, newY, currentY) = calculateWithdrawOneTokenDY( self, tokenIndex, tokenAmount, totalSupply ); // dy_0 (without fees) // dy, dy_0 - dy uint256 dySwapFee = currentY .sub(newY) .div(self.tokenPrecisionMultipliers[tokenIndex]) .sub(dy); return (dy, dySwapFee); } /** * @notice Calculate the dy of withdrawing in one token * @param self Swap struct to read from * @param tokenIndex which token will be withdrawn * @param tokenAmount the amount to withdraw in the pools precision * @return the d and the new y after withdrawing one token */ function calculateWithdrawOneTokenDY( Swap storage self, uint8 tokenIndex, uint256 tokenAmount, uint256 totalSupply ) internal view returns ( uint256, uint256, uint256 ) { // Get the current D, then solve the stableswap invariant // y_i for D - tokenAmount uint256[] memory xp = _xp(self); require(tokenIndex < xp.length, "Token index out of range"); CalculateWithdrawOneTokenDYInfo memory v = CalculateWithdrawOneTokenDYInfo(0, 0, 0, 0, 0); v.preciseA = _getAPrecise(self); v.d0 = getD(xp, v.preciseA); v.d1 = v.d0.sub(tokenAmount.mul(v.d0).div(totalSupply)); require(tokenAmount <= xp[tokenIndex], "Withdraw exceeds available"); v.newY = getYD(v.preciseA, tokenIndex, xp, v.d1); uint256[] memory xpReduced = new uint256[](xp.length); v.feePerToken = _feePerToken(self.swapFee, xp.length); for (uint256 i = 0; i < xp.length; i++) { uint256 xpi = xp[i]; // if i == tokenIndex, dxExpected = xp[i] * d1 / d0 - newY // else dxExpected = xp[i] - (xp[i] * d1 / d0) // xpReduced[i] -= dxExpected * fee / FEE_DENOMINATOR xpReduced[i] = xpi.sub( ( (i == tokenIndex) ? xpi.mul(v.d1).div(v.d0).sub(v.newY) : xpi.sub(xpi.mul(v.d1).div(v.d0)) ).mul(v.feePerToken).div(FEE_DENOMINATOR) ); } uint256 dy = xpReduced[tokenIndex].sub( getYD(v.preciseA, tokenIndex, xpReduced, v.d1) ); dy = dy.sub(1).div(self.tokenPrecisionMultipliers[tokenIndex]); return (dy, v.newY, xp[tokenIndex]); } /** * @notice Calculate the price of a token in the pool with given * precision-adjusted balances and a particular D. * * @dev This is accomplished via solving the invariant iteratively. * See the StableSwap paper and Curve.fi implementation for further details. * * x_1**2 + x1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A) * x_1**2 + b*x_1 = c * x_1 = (x_1**2 + c) / (2*x_1 + b) * * @param a the amplification coefficient * n * (n - 1). See the StableSwap paper for details. * @param tokenIndex Index of token we are calculating for. * @param xp a precision-adjusted set of pool balances. Array should be * the same cardinality as the pool. * @param d the stableswap invariant * @return the price of the token, in the same precision as in xp */ function getYD( uint256 a, uint8 tokenIndex, uint256[] memory xp, uint256 d ) internal pure returns (uint256) { uint256 numTokens = xp.length; require(tokenIndex < numTokens, "Token not found"); uint256 c = d; uint256 s; uint256 nA = a.mul(numTokens); for (uint256 i = 0; i < numTokens; i++) { if (i != tokenIndex) { s = s.add(xp[i]); c = c.mul(d).div(xp[i].mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // c = c * D * D * D * ... overflow! } } c = c.mul(d).mul(AmplificationUtils.A_PRECISION).div(nA.mul(numTokens)); uint256 b = s.add(d.mul(AmplificationUtils.A_PRECISION).div(nA)); uint256 yPrev; uint256 y = d; for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { yPrev = y; y = y.mul(y).add(c).div(y.mul(2).add(b).sub(d)); if (y.within1(yPrev)) { return y; } } revert("Approximation did not converge"); } /** * @notice Get D, the StableSwap invariant, based on a set of balances and a particular A. * @param xp a precision-adjusted set of pool balances. Array should be the same cardinality * as the pool. * @param a the amplification coefficient * n * (n - 1) in A_PRECISION. * See the StableSwap paper for details * @return the invariant, at the precision of the pool */ function getD(uint256[] memory xp, uint256 a) internal pure returns (uint256) { uint256 numTokens = xp.length; uint256 s; for (uint256 i = 0; i < numTokens; i++) { s = s.add(xp[i]); } if (s == 0) { return 0; } uint256 prevD; uint256 d = s; uint256 nA = a.mul(numTokens); for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { uint256 dP = d; for (uint256 j = 0; j < numTokens; j++) { dP = dP.mul(d).div(xp[j].mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // dP = dP * D * D * D * ... overflow! } prevD = d; d = nA .mul(s) .div(AmplificationUtils.A_PRECISION) .add(dP.mul(numTokens)) .mul(d) .div( nA .sub(AmplificationUtils.A_PRECISION) .mul(d) .div(AmplificationUtils.A_PRECISION) .add(numTokens.add(1).mul(dP)) ); if (d.within1(prevD)) { return d; } } // Convergence should occur in 4 loops or less. If this is reached, there may be something wrong // with the pool. If this were to occur repeatedly, LPs should withdraw via `removeLiquidity()` // function which does not rely on D. revert("D does not converge"); } /** * @notice Given a set of balances and precision multipliers, return the * precision-adjusted balances. * * @param balances an array of token balances, in their native precisions. * These should generally correspond with pooled tokens. * * @param precisionMultipliers an array of multipliers, corresponding to * the amounts in the balances array. When multiplied together they * should yield amounts at the pool's precision. * * @return an array of amounts "scaled" to the pool's precision */ function _xp( uint256[] memory balances, uint256[] memory precisionMultipliers ) internal pure returns (uint256[] memory) { uint256 numTokens = balances.length; require( numTokens == precisionMultipliers.length, "Balances must match multipliers" ); uint256[] memory xp = new uint256[](numTokens); for (uint256 i = 0; i < numTokens; i++) { xp[i] = balances[i].mul(precisionMultipliers[i]); } return xp; } /** * @notice Return the precision-adjusted balances of all tokens in the pool * @param self Swap struct to read from * @return the pool balances "scaled" to the pool's precision, allowing * them to be more easily compared. */ function _xp(Swap storage self) internal view returns (uint256[] memory) { return _xp(self.balances, self.tokenPrecisionMultipliers); } /** * @notice Get the virtual price, to help calculate profit * @param self Swap struct to read from * @return the virtual price, scaled to precision of POOL_PRECISION_DECIMALS */ function getVirtualPrice(Swap storage self) external view returns (uint256) { uint256 d = getD(_xp(self), _getAPrecise(self)); LPToken lpToken = self.lpToken; uint256 supply = lpToken.totalSupply(); if (supply > 0) { return d.mul(10**uint256(POOL_PRECISION_DECIMALS)).div(supply); } return 0; } /** * @notice Calculate the new balances of the tokens given the indexes of the token * that is swapped from (FROM) and the token that is swapped to (TO). * This function is used as a helper function to calculate how much TO token * the user should receive on swap. * * @param preciseA precise form of amplification coefficient * @param tokenIndexFrom index of FROM token * @param tokenIndexTo index of TO token * @param x the new total amount of FROM token * @param xp balances of the tokens in the pool * @return the amount of TO token that should remain in the pool */ function getY( uint256 preciseA, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 x, uint256[] memory xp ) internal pure returns (uint256) { uint256 numTokens = xp.length; require( tokenIndexFrom != tokenIndexTo, "Can't compare token to itself" ); require( tokenIndexFrom < numTokens && tokenIndexTo < numTokens, "Tokens must be in pool" ); uint256 d = getD(xp, preciseA); uint256 c = d; uint256 s; uint256 nA = numTokens.mul(preciseA); uint256 _x; for (uint256 i = 0; i < numTokens; i++) { if (i == tokenIndexFrom) { _x = x; } else if (i != tokenIndexTo) { _x = xp[i]; } else { continue; } s = s.add(_x); c = c.mul(d).div(_x.mul(numTokens)); // If we were to protect the division loss we would have to keep the denominator separate // and divide at the end. However this leads to overflow with large numTokens or/and D. // c = c * D * D * D * ... overflow! } c = c.mul(d).mul(AmplificationUtils.A_PRECISION).div(nA.mul(numTokens)); uint256 b = s.add(d.mul(AmplificationUtils.A_PRECISION).div(nA)); uint256 yPrev; uint256 y = d; // iterative approximation for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) { yPrev = y; y = y.mul(y).add(c).div(y.mul(2).add(b).sub(d)); if (y.within1(yPrev)) { return y; } } revert("Approximation did not converge"); } /** * @notice Externally calculates a swap between two tokens. * @param self Swap struct to read from * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @return dy the number of tokens the user will get */ function calculateSwap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256 dy) { (dy, ) = _calculateSwap( self, tokenIndexFrom, tokenIndexTo, dx, self.balances ); } /** * @notice Internally calculates a swap between two tokens. * * @dev The caller is expected to transfer the actual amounts (dx and dy) * using the token contracts. * * @param self Swap struct to read from * @param tokenIndexFrom the token to sell * @param tokenIndexTo the token to buy * @param dx the number of tokens to sell. If the token charges a fee on transfers, * use the amount that gets transferred after the fee. * @return dy the number of tokens the user will get * @return dyFee the associated fee */ function _calculateSwap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256[] memory balances ) internal view returns (uint256 dy, uint256 dyFee) { uint256[] memory multipliers = self.tokenPrecisionMultipliers; uint256[] memory xp = _xp(balances, multipliers); require( tokenIndexFrom < xp.length && tokenIndexTo < xp.length, "Token index out of range" ); uint256 x = dx.mul(multipliers[tokenIndexFrom]).add(xp[tokenIndexFrom]); uint256 y = getY( _getAPrecise(self), tokenIndexFrom, tokenIndexTo, x, xp ); dy = xp[tokenIndexTo].sub(y).sub(1); dyFee = dy.mul(self.swapFee).div(FEE_DENOMINATOR); dy = dy.sub(dyFee).div(multipliers[tokenIndexTo]); } /** * @notice A simple method to calculate amount of each underlying * tokens that is returned upon burning given amount of * LP tokens * * @param amount the amount of LP tokens that would to be burned on * withdrawal * @return array of amounts of tokens user will receive */ function calculateRemoveLiquidity(Swap storage self, uint256 amount) external view returns (uint256[] memory) { return _calculateRemoveLiquidity( self.balances, amount, self.lpToken.totalSupply() ); } function _calculateRemoveLiquidity( uint256[] memory balances, uint256 amount, uint256 totalSupply ) internal pure returns (uint256[] memory) { require(amount <= totalSupply, "Cannot exceed total supply"); uint256[] memory amounts = new uint256[](balances.length); for (uint256 i = 0; i < balances.length; i++) { amounts[i] = balances[i].mul(amount).div(totalSupply); } return amounts; } /** * @notice A simple method to calculate prices from deposits or * withdrawals, excluding fees but including slippage. This is * helpful as an input into the various "min" parameters on calls * to fight front-running * * @dev This shouldn't be used outside frontends for user estimates. * * @param self Swap struct to read from * @param amounts an array of token amounts to deposit or withdrawal, * corresponding to pooledTokens. The amount should be in each * pooled token's native precision. If a token charges a fee on transfers, * use the amount that gets transferred after the fee. * @param deposit whether this is a deposit or a withdrawal * @return if deposit was true, total amount of lp token that will be minted and if * deposit was false, total amount of lp token that will be burned */ function calculateTokenAmount( Swap storage self, uint256[] calldata amounts, bool deposit ) external view returns (uint256) { uint256 a = _getAPrecise(self); uint256[] memory balances = self.balances; uint256[] memory multipliers = self.tokenPrecisionMultipliers; uint256 d0 = getD(_xp(balances, multipliers), a); for (uint256 i = 0; i < balances.length; i++) { if (deposit) { balances[i] = balances[i].add(amounts[i]); } else { balances[i] = balances[i].sub( amounts[i], "Cannot withdraw more than available" ); } } uint256 d1 = getD(_xp(balances, multipliers), a); uint256 totalSupply = self.lpToken.totalSupply(); if (deposit) { return d1.sub(d0).mul(totalSupply).div(d0); } else { return d0.sub(d1).mul(totalSupply).div(d0); } } /** * @notice return accumulated amount of admin fees of the token with given index * @param self Swap struct to read from * @param index Index of the pooled token * @return admin balance in the token's precision */ function getAdminBalance(Swap storage self, uint256 index) external view returns (uint256) { require(index < self.pooledTokens.length, "Token index out of range"); return self.pooledTokens[index].balanceOf(address(this)).sub( self.balances[index] ); } /** * @notice internal helper function to calculate fee per token multiplier used in * swap fee calculations * @param swapFee swap fee for the tokens * @param numTokens number of tokens pooled */ function _feePerToken(uint256 swapFee, uint256 numTokens) internal pure returns (uint256) { return swapFee.mul(numTokens).div(numTokens.sub(1).mul(4)); } /*** STATE MODIFYING FUNCTIONS ***/ /** * @notice swap two tokens in the pool * @param self Swap struct to read from and write to * @param tokenIndexFrom the token the user wants to sell * @param tokenIndexTo the token the user wants to buy * @param dx the amount of tokens the user wants to sell * @param minDy the min amount the user would like to receive, or revert. * @return amount of token user received on swap */ function swap( Swap storage self, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy ) external returns (uint256) { { IERC20 tokenFrom = self.pooledTokens[tokenIndexFrom]; require( dx <= tokenFrom.balanceOf(msg.sender), "Cannot swap more than you own" ); // Transfer tokens first to see if a fee was charged on transfer uint256 beforeBalance = tokenFrom.balanceOf(address(this)); tokenFrom.safeTransferFrom(msg.sender, address(this), dx); // Use the actual transferred amount for AMM math dx = tokenFrom.balanceOf(address(this)).sub(beforeBalance); } uint256 dy; uint256 dyFee; uint256[] memory balances = self.balances; (dy, dyFee) = _calculateSwap( self, tokenIndexFrom, tokenIndexTo, dx, balances ); require(dy >= minDy, "Swap didn't result in min tokens"); uint256 dyAdminFee = dyFee.mul(self.adminFee).div(FEE_DENOMINATOR).div( self.tokenPrecisionMultipliers[tokenIndexTo] ); self.balances[tokenIndexFrom] = balances[tokenIndexFrom].add(dx); self.balances[tokenIndexTo] = balances[tokenIndexTo].sub(dy).sub( dyAdminFee ); self.pooledTokens[tokenIndexTo].safeTransfer(msg.sender, dy); emit TokenSwap(msg.sender, dx, dy, tokenIndexFrom, tokenIndexTo); return dy; } /** * @notice Add liquidity to the pool * @param self Swap struct to read from and write to * @param amounts the amounts of each token to add, in their native precision * @param minToMint the minimum LP tokens adding this amount of liquidity * should mint, otherwise revert. Handy for front-running mitigation * allowed addresses. If the pool is not in the guarded launch phase, this parameter will be ignored. * @return amount of LP token user received */ function addLiquidity( Swap storage self, uint256[] memory amounts, uint256 minToMint ) external returns (uint256) { IERC20[] memory pooledTokens = self.pooledTokens; require( amounts.length == pooledTokens.length, "Amounts must match pooled tokens" ); // current state ManageLiquidityInfo memory v = ManageLiquidityInfo( 0, 0, 0, _getAPrecise(self), self.lpToken, 0, self.balances, self.tokenPrecisionMultipliers ); v.totalSupply = v.lpToken.totalSupply(); if (v.totalSupply != 0) { v.d0 = getD(_xp(v.balances, v.multipliers), v.preciseA); } uint256[] memory newBalances = new uint256[](pooledTokens.length); for (uint256 i = 0; i < pooledTokens.length; i++) { require( v.totalSupply != 0 || amounts[i] > 0, "Must supply all tokens in pool" ); // Transfer tokens first to see if a fee was charged on transfer if (amounts[i] != 0) { uint256 beforeBalance = pooledTokens[i].balanceOf( address(this) ); pooledTokens[i].safeTransferFrom( msg.sender, address(this), amounts[i] ); // Update the amounts[] with actual transfer amount amounts[i] = pooledTokens[i].balanceOf(address(this)).sub( beforeBalance ); } newBalances[i] = v.balances[i].add(amounts[i]); } // invariant after change v.d1 = getD(_xp(newBalances, v.multipliers), v.preciseA); require(v.d1 > v.d0, "D should increase"); // updated to reflect fees and calculate the user's LP tokens v.d2 = v.d1; uint256[] memory fees = new uint256[](pooledTokens.length); if (v.totalSupply != 0) { uint256 feePerToken = _feePerToken( self.swapFee, pooledTokens.length ); for (uint256 i = 0; i < pooledTokens.length; i++) { uint256 idealBalance = v.d1.mul(v.balances[i]).div(v.d0); fees[i] = feePerToken .mul(idealBalance.difference(newBalances[i])) .div(FEE_DENOMINATOR); self.balances[i] = newBalances[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); newBalances[i] = newBalances[i].sub(fees[i]); } v.d2 = getD(_xp(newBalances, v.multipliers), v.preciseA); } else { // the initial depositor doesn't pay fees self.balances = newBalances; } uint256 toMint; if (v.totalSupply == 0) { toMint = v.d1; } else { toMint = v.d2.sub(v.d0).mul(v.totalSupply).div(v.d0); } require(toMint >= minToMint, "Couldn't mint min requested"); // mint the user's LP tokens v.lpToken.mint(msg.sender, toMint); emit AddLiquidity( msg.sender, amounts, fees, v.d1, v.totalSupply.add(toMint) ); return toMint; } /** * @notice Burn LP tokens to remove liquidity from the pool. * @dev Liquidity can always be removed, even when the pool is paused. * @param self Swap struct to read from and write to * @param amount the amount of LP tokens to burn * @param minAmounts the minimum amounts of each token in the pool * acceptable for this burn. Useful as a front-running mitigation * @return amounts of tokens the user received */ function removeLiquidity( Swap storage self, uint256 amount, uint256[] calldata minAmounts ) external returns (uint256[] memory) { LPToken lpToken = self.lpToken; IERC20[] memory pooledTokens = self.pooledTokens; require(amount <= lpToken.balanceOf(msg.sender), ">LP.balanceOf"); require( minAmounts.length == pooledTokens.length, "minAmounts must match poolTokens" ); uint256[] memory balances = self.balances; uint256 totalSupply = lpToken.totalSupply(); uint256[] memory amounts = _calculateRemoveLiquidity( balances, amount, totalSupply ); for (uint256 i = 0; i < amounts.length; i++) { require(amounts[i] >= minAmounts[i], "amounts[i] < minAmounts[i]"); self.balances[i] = balances[i].sub(amounts[i]); pooledTokens[i].safeTransfer(msg.sender, amounts[i]); } lpToken.burnFrom(msg.sender, amount); emit RemoveLiquidity(msg.sender, amounts, totalSupply.sub(amount)); return amounts; } /** * @notice Remove liquidity from the pool all in one token. * @param self Swap struct to read from and write to * @param tokenAmount the amount of the lp tokens to burn * @param tokenIndex the index of the token you want to receive * @param minAmount the minimum amount to withdraw, otherwise revert * @return amount chosen token that user received */ function removeLiquidityOneToken( Swap storage self, uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount ) external returns (uint256) { LPToken lpToken = self.lpToken; IERC20[] memory pooledTokens = self.pooledTokens; require(tokenAmount <= lpToken.balanceOf(msg.sender), ">LP.balanceOf"); require(tokenIndex < pooledTokens.length, "Token not found"); uint256 totalSupply = lpToken.totalSupply(); (uint256 dy, uint256 dyFee) = _calculateWithdrawOneToken( self, tokenAmount, tokenIndex, totalSupply ); require(dy >= minAmount, "dy < minAmount"); self.balances[tokenIndex] = self.balances[tokenIndex].sub( dy.add(dyFee.mul(self.adminFee).div(FEE_DENOMINATOR)) ); lpToken.burnFrom(msg.sender, tokenAmount); pooledTokens[tokenIndex].safeTransfer(msg.sender, dy); emit RemoveLiquidityOne( msg.sender, tokenAmount, totalSupply, tokenIndex, dy ); return dy; } /** * @notice Remove liquidity from the pool, weighted differently than the * pool's current balances. * * @param self Swap struct to read from and write to * @param amounts how much of each token to withdraw * @param maxBurnAmount the max LP token provider is willing to pay to * remove liquidity. Useful as a front-running mitigation. * @return actual amount of LP tokens burned in the withdrawal */ function removeLiquidityImbalance( Swap storage self, uint256[] memory amounts, uint256 maxBurnAmount ) public returns (uint256) { ManageLiquidityInfo memory v = ManageLiquidityInfo( 0, 0, 0, _getAPrecise(self), self.lpToken, 0, self.balances, self.tokenPrecisionMultipliers ); v.totalSupply = v.lpToken.totalSupply(); IERC20[] memory pooledTokens = self.pooledTokens; require( amounts.length == pooledTokens.length, "Amounts should match pool tokens" ); require( maxBurnAmount <= v.lpToken.balanceOf(msg.sender) && maxBurnAmount != 0, ">LP.balanceOf" ); uint256 feePerToken = _feePerToken(self.swapFee, pooledTokens.length); uint256[] memory fees = new uint256[](pooledTokens.length); { uint256[] memory balances1 = new uint256[](pooledTokens.length); v.d0 = getD(_xp(v.balances, v.multipliers), v.preciseA); for (uint256 i = 0; i < pooledTokens.length; i++) { balances1[i] = v.balances[i].sub( amounts[i], "Cannot withdraw more than available" ); } v.d1 = getD(_xp(balances1, v.multipliers), v.preciseA); for (uint256 i = 0; i < pooledTokens.length; i++) { uint256 idealBalance = v.d1.mul(v.balances[i]).div(v.d0); uint256 difference = idealBalance.difference(balances1[i]); fees[i] = feePerToken.mul(difference).div(FEE_DENOMINATOR); self.balances[i] = balances1[i].sub( fees[i].mul(self.adminFee).div(FEE_DENOMINATOR) ); balances1[i] = balances1[i].sub(fees[i]); } v.d2 = getD(_xp(balances1, v.multipliers), v.preciseA); } uint256 tokenAmount = v.d0.sub(v.d2).mul(v.totalSupply).div(v.d0); require(tokenAmount != 0, "Burnt amount cannot be zero"); tokenAmount = tokenAmount.add(1); require(tokenAmount <= maxBurnAmount, "tokenAmount > maxBurnAmount"); v.lpToken.burnFrom(msg.sender, tokenAmount); for (uint256 i = 0; i < pooledTokens.length; i++) { pooledTokens[i].safeTransfer(msg.sender, amounts[i]); } emit RemoveLiquidityImbalance( msg.sender, amounts, fees, v.d1, v.totalSupply.sub(tokenAmount) ); return tokenAmount; } /** * @notice withdraw all admin fees to a given address * @param self Swap struct to withdraw fees from * @param to Address to send the fees to */ function withdrawAdminFees(Swap storage self, address to) external { IERC20[] memory pooledTokens = self.pooledTokens; for (uint256 i = 0; i < pooledTokens.length; i++) { IERC20 token = pooledTokens[i]; uint256 balance = token.balanceOf(address(this)).sub( self.balances[i] ); if (balance != 0) { token.safeTransfer(to, balance); } } } /** * @notice Sets the admin fee * @dev adminFee cannot be higher than 100% of the swap fee * @param self Swap struct to update * @param newAdminFee new admin fee to be applied on future transactions */ function setAdminFee(Swap storage self, uint256 newAdminFee) external { require(newAdminFee <= MAX_ADMIN_FEE, "Fee is too high"); self.adminFee = newAdminFee; emit NewAdminFee(newAdminFee); } /** * @notice update the swap fee * @dev fee cannot be higher than 1% of each swap * @param self Swap struct to update * @param newSwapFee new swap fee to be applied on future transactions */ function setSwapFee(Swap storage self, uint256 newSwapFee) external { require(newSwapFee <= MAX_SWAP_FEE, "Fee is too high"); self.swapFee = newSwapFee; emit NewSwapFee(newSwapFee); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IAllowlist { function getPoolAccountLimit(address poolAddress) external view returns (uint256); function getPoolCap(address poolAddress) external view returns (uint256); function verifyAddress(address account, bytes32[] calldata merkleProof) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "./IAllowlist.sol"; interface ISwap { // pool data view functions function getA() external view returns (uint256); function getAllowlist() external view returns (IAllowlist); function getToken(uint8 index) external view returns (IERC20); function getTokenIndex(address tokenAddress) external view returns (uint8); function getTokenBalance(uint8 index) external view returns (uint256); function getVirtualPrice() external view returns (uint256); function isGuarded() external view returns (bool); function swapStorage() external view returns ( uint256, uint256, uint256, uint256, uint256, uint256, address ); // min return calculation functions function calculateSwap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx ) external view returns (uint256); function calculateTokenAmount(uint256[] calldata amounts, bool deposit) external view returns (uint256); function calculateRemoveLiquidity(uint256 amount) external view returns (uint256[] memory); function calculateRemoveLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex ) external view returns (uint256 availableTokenAmount); // state modifying functions function initialize( IERC20[] memory pooledTokens, uint8[] memory decimals, string memory lpTokenName, string memory lpTokenSymbol, uint256 a, uint256 fee, uint256 adminFee, address lpTokenTargetAddress ) external; function swap( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline ) external returns (uint256); function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline ) external returns (uint256); function removeLiquidity( uint256 amount, uint256[] calldata minAmounts, uint256 deadline ) external returns (uint256[] memory); function removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline ) external returns (uint256); function removeLiquidityImbalance( uint256[] calldata amounts, uint256 maxBurnAmount, uint256 deadline ) external returns (uint256); }
{ "evmVersion": "istanbul", "libraries": {}, "metadata": { "bytecodeHash": "ipfs", "useLiteralContent": true }, "optimizer": { "enabled": true, "runs": 10000 }, "remappings": [], "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"fees","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"invariant","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"name":"AddLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newAdminFee","type":"uint256"}],"name":"NewAdminFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newSwapFee","type":"uint256"}],"name":"NewSwapFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newWithdrawFee","type":"uint256"}],"name":"NewWithdrawFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokensSold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensBought","type":"uint256"},{"indexed":false,"internalType":"uint128","name":"soldId","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"boughtId","type":"uint128"}],"name":"TokenSwapUnderlying","type":"event"},{"inputs":[],"name":"BASE_CACHE_EXPIRE_TIME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BASE_VIRTUAL_PRICE_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
614b42610026600b82828239805160001a60731461001957fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106100dd5760003560e01c8063becf967c1161008b578063d45757ba11610065578063d45757ba1461041d578063e9d4d07714610459578063f6ada8c21461047c576100dd565b8063becf967c14610312578063c1a662731461031a578063d1ab4b4a1461035f576100dd565b80637e971656116100bc5780637e971656146101fd57806398414eed146102bb578063a28c23411461030a576100dd565b806280247a146100e25780635afb90ff1461014357806378b0bcbe1461017f575b600080fd5b8180156100ee57600080fd5b50610131600480360360c081101561010557600080fd5b5080359060208101359060ff604082013581169160608101359091169060808101359060a001356104ae565b60408051918252519081900360200190f35b610131600480360360a081101561015957600080fd5b5080359060208101359060ff604082013581169160608101359091169060800135611162565b6101316004803603608081101561019557600080fd5b8135916020810135918101906060810160408201356401000000008111156101bc57600080fd5b8201836020820111156101ce57600080fd5b803590602001918460208302840111640100000000831117156101f057600080fd5b9193509150351515611184565b81801561020957600080fd5b506101316004803603608081101561022057600080fd5b81359160208101359181019060608101604082013564010000000081111561024757600080fd5b82018360208201111561025957600080fd5b8035906020019184602083028401116401000000008311171561027b57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250611408915050565b8180156102c757600080fd5b50610131600480360360c08110156102de57600080fd5b5080359060208101359060ff604082013581169160608101359091169060808101359060a00135611b9d565b610131611fb7565b610131611fc3565b81801561032657600080fd5b50610131600480360360a081101561033d57600080fd5b5080359060208101359060408101359060ff6060820135169060800135611fc9565b81801561036b57600080fd5b506101316004803603608081101561038257600080fd5b8135916020810135918101906060810160408201356401000000008111156103a957600080fd5b8201836020820111156103bb57600080fd5b803590602001918460208302840111640100000000831117156103dd57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250612355915050565b610131600480360360a081101561043357600080fd5b5080359060208101359060ff604082013581169160608101359091169060800135612bb6565b6101316004803603604081101561046f57600080fd5b5080359060200135613139565b6101316004803603608081101561049257600080fd5b508035906020810135906040810135906060013560ff166132b8565b60006104b86148df565b6040518061016001604052806000815260200160008152602001600081526020018960080180548060200260200160405190810160405280929190818152602001828054801561052757602002820191906000526020600020905b815481526020019060010190808311610513575b505050505081526020018960090180548060200260200160405190810160405280929190818152602001828054801561057f57602002820191906000526020600020905b81548152602001906001019080831161056b575b50505050508152602001886003018054806020026020016040519081016040528092919081815260200182805480156105e157602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116105c3575b50505091835250506000602082018190526040820181905260608201819052608082015260a00161061189613354565b905260808101515190915060009061062a906001613411565b905060008260a00151518260ff160190508060ff168860ff1610801561065557508060ff168760ff16105b6106a6576040805162461bcd60e51b815260206004820152601860248201527f546f6b656e20696e646578206f7574206f662072616e67650000000000000000604482015290519081900360640190fd5b5087546001600160a01b031660ff80831690891610156106fa57896007018860ff16815481106106d257fe5b6000918252602090912001546001600160a01b031660c084015260ff881660e0840152610732565b8260a0015182890360ff168151811061070f57fe5b60209081029190910101516001600160a01b031660c084015260ff821660e08401525b8160ff168760ff16101561077c57896007018760ff168154811061075257fe5b6000918252602090912001546001600160a01b031661010084015260ff87166101208401526107b6565b8260a0015182880360ff168151811061079157fe5b60209081029190910101516001600160a01b031661010084015260ff82166101208401525b8260c001516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561080757600080fd5b505afa15801561081b573d6000803e3d6000fd5b505050506040513d602081101561083157600080fd5b5051602084015260c0830151610852906001600160a01b031633308961346e565b6108dd83602001518460c001516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156108ab57600080fd5b505afa1580156108bf573d6000803e3d6000fd5b505050506040513d60208110156108d557600080fd5b505190613411565b602084015260ff80831690891610806108fb57508160ff168760ff16105b15610f2b57606061091a846080015185606001518661014001516134fc565b90508260ff168960ff1610156109865761097f61095a85606001518b60ff168151811061094357fe5b60200260200101518961356290919063ffffffff16565b828b60ff168151811061096957fe5b60200260200101516135bb90919063ffffffff16565b8452610b0e565b60608460a001515167ffffffffffffffff811180156109a457600080fd5b506040519080825280602002602001820160405280156109ce578160200160208202803683370190505b509050846020015181858c0360ff16815181106109e757fe5b602002602001018181525050826001600160a01b0316634d49e87d826000426040518463ffffffff1660e01b81526004018080602001848152602001838152602001828103825285818151815260200191508051906020019060200280838360005b83811015610a61578181015183820152602001610a49565b50505050905001945050505050602060405180830381600087803b158015610a8857600080fd5b505af1158015610a9c573d6000803e3d6000fd5b505050506040513d6020811015610ab257600080fd5b505160208601528151610b0a90839060ff8716908110610ace57fe5b6020026020010151610b04670de0b6b3a7640000610afe8961014001518a6020015161356290919063ffffffff16565b90613615565b906135bb565b8552505b600080610b30610b1d8e61367c565b60e0880151610120890151895187613711565b9050610b6b6001610b6583868a610120015160ff1681518110610b4f57fe5b602002602001015161341190919063ffffffff16565b90613411565b604087015260ff808616908b1610610bab57610ba5866101400151610afe670de0b6b3a7640000896040015161356290919063ffffffff16565b60408701525b610bcf6402540be400610afe8f60040154896040015161356290919063ffffffff16565b9150610c0b866060015187610120015160ff1681518110610bec57fe5b6020026020010151610afe84896040015161341190919063ffffffff16565b60408701525060058c0154600090610c2f906402540be40090610afe908590613562565b9050610c63866060015187610120015160ff1681518110610c4c57fe5b60200260200101518261361590919063ffffffff16565b9050610c84866020015187608001518860e0015160ff168151811061096957fe5b8d6009018760e0015160ff1681548110610c9a57fe5b9060005260206000200181905550610ccc81610b65886040015189608001518a610120015160ff1681518110610b4f57fe5b8d60090187610120015160ff1681548110610ce357fe5b9060005260206000200181905550508360ff168960ff1610610ecb5760008561010001516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610d5357600080fd5b505afa158015610d67573d6000803e3d6000fd5b505050506040513d6020811015610d7d57600080fd5b505160408088015181517f3e3a1560000000000000000000000000000000000000000000000000000000008152600481019190915260ff888e0316602482015260006044820181905242606483015291519293506001600160a01b03871692633e3a156092608480840193602093929083900390910190829087803b158015610e0557600080fd5b505af1158015610e19573d6000803e3d6000fd5b505050506040513d6020811015610e2f57600080fd5b5050610100860151604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905183926001600160a01b0316916370a08231916024808301926020929190829003018186803b158015610e9757600080fd5b505afa158015610eab573d6000803e3d6000fd5b505050506040513d6020811015610ec157600080fd5b5051036040870152505b8685604001511015610f24576040805162461bcd60e51b815260206004820181905260248201527f53776170206469646e277420726573756c7420696e206d696e20746f6b656e73604482015290519081900360640190fd5b50506110d8565b8261010001516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610f7d57600080fd5b505afa158015610f91573d6000803e3d6000fd5b505050506040513d6020811015610fa757600080fd5b505160408085019190915260208481015182517f9169558600000000000000000000000000000000000000000000000000000000815260ff868d0381166004830152868c0316602482015260448101919091526064810188905242608482015291516001600160a01b0384169263916955869260a48083019391928290030181600087803b15801561103857600080fd5b505af115801561104c573d6000803e3d6000fd5b505050506040513d602081101561106257600080fd5b505060408084015161010085015182517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015292516110d2936001600160a01b03909216916370a08231916024808301926020929190829003018186803b1580156108ab57600080fd5b60408401525b6110ff3384604001518561010001516001600160a01b03166139579092919063ffffffff16565b6040808401518151888152602081019190915260ff808b168284015289166060820152905133917f6617207207e397b41fc98016d8c9febb7223f44c355db66ad429730f2b950a60919081900360800190a2505060400151979650505050505050565b6000611179868585856111748a6139dc565b613a7e565b509695505050505050565b6000806111908761367c565b905060008060006111a0896139dc565b905060608a6009018054806020026020016040519081016040528092919081815260200182805480156111f257602002820191906000526020600020905b8154815260200190600101908083116111de575b5050505050905060608b60080180548060200260200160405190810160405280929190818152602001828054801561124957602002820191906000526020600020905b815481526020019060010190808311611235575b5050505050905060008251905061126a6112648484876134fc565b88613c4e565b955060005b8181101561132f5789156112bc5761129f8c8c8381811061128c57fe5b9050602002013585838151811061096957fe5b8482815181106112ab57fe5b602002602001018181525050611327565b61130e8c8c838181106112cb57fe5b90506020020135604051806060016040528060238152602001614aea602391398684815181106112f757fe5b6020026020010151613db49092919063ffffffff16565b84828151811061131a57fe5b6020026020010181815250505b60010161126f565b5061133e6112648484876134fc565b94505050505060008960060160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561139657600080fd5b505afa1580156113aa573d6000803e3d6000fd5b505050506040513d60208110156113c057600080fd5b5051905085156113ed576113e283610afe836113dc8684613411565b90613562565b9450505050506113ff565b6113e283610afe836113dc8387613411565b95945050505050565b6000611412614951565b604080516101208101825260008082526020820181905291810182905260068801546001600160a01b03166060820152608081019190915260a081016114578861367c565b815260200161146587613354565b8152602001876008018054806020026020016040519081016040528092919081815260200182805480156114b857602002820191906000526020600020905b8154815260200190600101908083116114a4575b505050505081526020018760090180548060200260200160405190810160405280929190818152602001828054801561151057602002820191906000526020600020905b8154815260200190600101908083116114fc575b5050505050815250905080606001516001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561155757600080fd5b505afa15801561156b573d6000803e3d6000fd5b505050506040513d602081101561158157600080fd5b50516080820152610100810151518451146115e3576040805162461bcd60e51b815260206004820181905260248201527f416d6f756e74732073686f756c64206d6174636820706f6f6c20746f6b656e73604482015290519081900360640190fd5b82611635576040805162461bcd60e51b815260206004820152601560248201527f4d757374206275726e206d6f7265207468616e20300000000000000000000000604482015290519081900360640190fd5b600061164b876004015483610100015151613e4b565b905060608261010001515167ffffffffffffffff8111801561166c57600080fd5b50604051908082528060200260200182016040528015611696578160200160208202803683370190505b50905060608361010001515167ffffffffffffffff811180156116b857600080fd5b506040519080825280602002602001820160405280156116e2578160200160208202803683370190505b50905061170a6117008561010001518660e001518760c001516134fc565b8560a00151613c4e565b845260005b8461010001515181101561177d5761175e88828151811061172c57fe5b6020026020010151604051806060016040528060238152602001614aea6023913987610100015184815181106112f757fe5b82828151811061176a57fe5b602090810291909101015260010161170f565b50611794611700828660e001518760c001516134fc565b602085015260005b846101000151518110156118dd5760006117e38660000151610afe88610100015185815181106117c857fe5b6020026020010151896020015161356290919063ffffffff16565b9050600061180d8484815181106117f657fe5b602002602001015183613e6a90919063ffffffff16565b90506118226402540be400610afe8884613562565b85848151811061182e57fe5b60200260200101818152505061187c6118706402540be400610afe8f6005015489888151811061185a57fe5b602002602001015161356290919063ffffffff16565b858581518110610b4f57fe5b8c600901848154811061188b57fe5b90600052602060002001819055506118bc8584815181106118a857fe5b6020026020010151858581518110610b4f57fe5b8484815181106118c857fe5b6020908102919091010152505060010161179c565b506118f4611700828660e001518760c001516134fc565b60408501819052845160808601516000935061191a92610afe91906113dc908490613411565b90508061196e576040805162461bcd60e51b815260206004820152601b60248201527f4275726e7420616d6f756e742063616e6e6f74206265207a65726f0000000000604482015290519081900360640190fd5b6119798160016135bb565b9050858111156119d0576040805162461bcd60e51b815260206004820152601b60248201527f746f6b656e416d6f756e74203e206d61784275726e416d6f756e740000000000604482015290519081900360640190fd5b83606001516001600160a01b03166379cc679033836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015611a2b57600080fd5b505af1158015611a3f573d6000803e3d6000fd5b5050505060005b84610100015151811015611a9f57611a9733898381518110611a6457fe5b60200260200101518c6007018481548110611a7b57fe5b6000918252602090912001546001600160a01b03169190613957565b600101611a46565b50336001600160a01b03167f3631c28b1f9dd213e0319fb167b554d76b6c283a41143eb400a0d1adb1af175588848760200151611ae9868a6080015161341190919063ffffffff16565b604051808060200180602001858152602001848152602001838103835287818151815260200191508051906020019060200280838360005b83811015611b39578181015183820152602001611b21565b50505050905001838103825286818151815260200191508051906020019060200280838360005b83811015611b78578181015183820152602001611b60565b50505050905001965050505050505060405180910390a293505050505b949350505050565b600786015460009060ff861681118015611bb95750808560ff16105b611c0a576040805162461bcd60e51b815260206004820152601b60248201527f546f6b656e20696e646578206973206f7574206f662072616e67650000000000604482015290519081900360640190fd5b50600080886007018760ff1681548110611c2057fe5b60009182526020918290200154604080517f70a0823100000000000000000000000000000000000000000000000000000000815233600482015290516001600160a01b03909216935083926370a0823192602480840193829003018186803b158015611c8b57600080fd5b505afa158015611c9f573d6000803e3d6000fd5b505050506040513d6020811015611cb557600080fd5b5051851115611d0b576040805162461bcd60e51b815260206004820152601d60248201527f43616e6e6f742073776170206d6f7265207468616e20796f75206f776e000000604482015290519081900360640190fd5b6000816001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015611d5a57600080fd5b505afa158015611d6e573d6000803e3d6000fd5b505050506040513d6020811015611d8457600080fd5b50519050611d9d6001600160a01b03831633308961346e565b611dee81836001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156108ab57600080fd5b92505050600080611e058a8989866111748e613354565b9150915084821015611e5e576040805162461bcd60e51b815260206004820181905260248201527f53776170206469646e277420726573756c7420696e206d696e20746f6b656e73604482015290519081900360640190fd5b6000611ea08b6008018960ff1681548110611e7557fe5b9060005260206000200154610afe6402540be400610afe8f600501548761356290919063ffffffff16565b9050611ed1848c6009018b60ff1681548110611eb857fe5b90600052602060002001546135bb90919063ffffffff16565b8b6009018a60ff1681548110611ee357fe5b9060005260206000200181905550611f2481610b65858e6009018c60ff1681548110611f0b57fe5b906000526020600020015461341190919063ffffffff16565b8b6009018960ff1681548110611f3657fe5b9060005260206000200181905550611f5b33848d6007018b60ff1681548110611a7b57fe5b604080518581526020810185905260ff808c16828401528a166060820152905133917fc6c1e0630dbe9130cc068028486c0d118ddcea348550819defd5cb8c257f8a38919081900360800190a250909998505050505050505050565b670de0b6b3a764000081565b61025881565b6006850154604080517f18160ddd00000000000000000000000000000000000000000000000000000000815290516000926001600160a01b031691839183916318160ddd916004808301926020929190829003018186803b15801561202d57600080fd5b505afa158015612041573d6000803e3d6000fd5b505050506040513d602081101561205757600080fd5b50516007890154604080517f70a08231000000000000000000000000000000000000000000000000000000008152336004820152905192935090916001600160a01b038516916370a08231916024808301926020929190829003018186803b1580156120c257600080fd5b505afa1580156120d6573d6000803e3d6000fd5b505050506040513d60208110156120ec57600080fd5b5051871115612142576040805162461bcd60e51b815260206004820152600d60248201527f3e4c502e62616c616e63654f6600000000000000000000000000000000000000604482015290519081900360640190fd5b808660ff1610612199576040805162461bcd60e51b815260206004820152600f60248201527f546f6b656e206e6f7420666f756e640000000000000000000000000000000000604482015290519081900360640190fd5b6000806121b18b8a8a6121ab8e613354565b88613e82565b925090508681101561220a576040805162461bcd60e51b815260206004820152600e60248201527f6479203c206d696e416d6f756e74000000000000000000000000000000000000604482015290519081900360640190fd5b6122496122376122306402540be400610afe8f600501548761356290919063ffffffff16565b83906135bb565b8c6009018a60ff1681548110611f0b57fe5b8b6009018960ff168154811061225b57fe5b6000918252602082200191909155604080517f79cc6790000000000000000000000000000000000000000000000000000000008152336004820152602481018c905290516001600160a01b038816926379cc6790926044808201939182900301818387803b1580156122cc57600080fd5b505af11580156122e0573d6000803e3d6000fd5b505050506122fb33828d6007018b60ff1681548110611a7b57fe5b604080518a81526020810186905260ff8a168183015260608101839052905133917f43fb02998f4e03da2e0e6fff53fdbf0c40a9f45f145dc377fc30615d7d7a8a64919081900360800190a29a9950505050505050505050565b60006060856007018054806020026020016040519081016040528092919081815260200182805480156123b157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612393575b50505050509050805184511461240e576040805162461bcd60e51b815260206004820181905260248201527f416d6f756e7473206d757374206d6174636820706f6f6c656420746f6b656e73604482015290519081900360640190fd5b6060815167ffffffffffffffff8111801561242857600080fd5b50604051908082528060200260200182016040528015612452578160200160208202803683370190505b50905061245d614951565b604080516101208101825260008082526020820181905291810182905260068a01546001600160a01b03166060820152608081019190915260a081016124a28a61367c565b81526020016124b089613354565b81526020018960080180548060200260200160405190810160405280929190818152602001828054801561250357602002820191906000526020600020905b8154815260200190600101908083116124ef575b505050505081526020018960090180548060200260200160405190810160405280929190818152602001828054801561255b57602002820191906000526020600020905b815481526020019060010190808311612547575b5050505050815250905080606001516001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156125a257600080fd5b505afa1580156125b6573d6000803e3d6000fd5b505050506040513d60208110156125cc57600080fd5b50516080820181905215612602576125ff6125f58261010001518360e001518460c001516134fc565b8260a00151613c4e565b81525b60005b83518110156128455760808201511515806126335750600087828151811061262957fe5b6020026020010151115b612684576040805162461bcd60e51b815260206004820152601e60248201527f4d75737420737570706c7920616c6c20746f6b656e7320696e20706f6f6c0000604482015290519081900360640190fd5b86818151811061269057fe5b60200260200101516000146127f95760008482815181106126ad57fe5b60200260200101516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561270157600080fd5b505afa158015612715573d6000803e3d6000fd5b505050506040513d602081101561272b57600080fd5b5051885190915061277b90339030908b908690811061274657fe5b602002602001015188868151811061275a57fe5b60200260200101516001600160a01b031661346e909392919063ffffffff16565b6127df8186848151811061278b57fe5b60200260200101516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156108ab57600080fd5b8883815181106127eb57fe5b602002602001018181525050505b61282187828151811061280857fe5b6020026020010151836101000151838151811061096957fe5b826101000151828151811061283257fe5b6020908102919091010152600101612605565b506128616125f58261010001518360e001518460c001516134fc565b602082018190528151106128bc576040805162461bcd60e51b815260206004820152601160248201527f442073686f756c6420696e637265617365000000000000000000000000000000604482015290519081900360640190fd5b60208101516040820152608081015160009015612a6e5760006128e48a600401548651613e4b565b905060005b8551811015612a1f57600061292c8560000151610afe8e600901858154811061290e57fe5b9060005260206000200154886020015161356290919063ffffffff16565b905061296c6402540be400610afe612965886101000151868151811061294e57fe5b602002602001015185613e6a90919063ffffffff16565b8690613562565b86838151811061297857fe5b6020026020010181815250506129b56129a46402540be400610afe8f600501548a878151811061185a57fe5b8661010001518481518110610b4f57fe5b8c60090183815481106129c457fe5b90600052602060002001819055506129fa8683815181106129e157fe5b60200260200101518661010001518481518110610b4f57fe5b8561010001518381518110612a0b57fe5b6020908102919091010152506001016128e9565b50612a45612a3b8461010001518560e001518660c001516134fc565b8460a00151613c4e565b6040840181905283516080850151612a6692610afe91906113dc9084613411565b915050612a92565b6101008201518051612a8a9160098c01916020909101906149a6565b505060208101515b85811015612ae7576040805162461bcd60e51b815260206004820152601b60248201527f436f756c646e2774206d696e74206d696e207265717565737465640000000000604482015290519081900360640190fd5b6006890154604080517f40c10f190000000000000000000000000000000000000000000000000000000081523360048201526024810184905290516001600160a01b03909216916340c10f199160448082019260009290919082900301818387803b158015612b5557600080fd5b505af1158015612b69573d6000803e3d6000fd5b50505050336001600160a01b03167f189c623b666b1b45b83d7178f39b8c087cb09774317ca2f53c2d3c3726f222a288858560200151611ae98688608001516135bb90919063ffffffff16565b6000612bc06149f1565b6040518060e00160405280612bd4886139dc565b815287546001600160a01b03166020820152600060408201819052600389015460ff166060808401919091526080830182905260a0830182905260c090920152815191925090612c25908990613ee5565b8051909150612c35906001613411565b60ff9081166040840181905260608401510190818116908816108015612c6057508060ff168660ff16105b612cb1576040805162461bcd60e51b815260206004820152601860248201527f546f6b656e20696e646578206f7574206f662072616e67650000000000000000604482015290519081900360640190fd5b50816040015160ff168660ff161015612d0f57612d05612cf6896008018860ff1681548110612cdc57fe5b90600052602060002001548661356290919063ffffffff16565b828860ff168151811061096957fe5b60a0830152612f88565b6040820151958690039560ff9081169086161015612ee6576060826060015160ff1667ffffffffffffffff81118015612d4757600080fd5b50604051908082528060200260200182016040528015612d71578160200160208202803683370190505b50905084818860ff1681518110612d8457fe5b602002602001018181525050612e67670de0b6b3a7640000610afe856000015186602001516001600160a01b031663e6ab28068660016040518363ffffffff1660e01b815260040180806020018315158152602001828103825284818151815260200191508051906020019060200280838360005b83811015612e11578181015183820152602001612df9565b50505050905001935050505060206040518083038186803b158015612e3557600080fd5b505afa158015612e49573d6000803e3d6000fd5b505050506040513d6020811015612e5f57600080fd5b505190613562565b8360a0018181525050612edb82846040015160ff1681518110612e8657fe5b6020026020010151610b04612ed0612ead60026402540be40061356290919063ffffffff16565b8c54610afe90612ec5906001600160a01b0316613f96565b60a08a015190613562565b60a087015190613411565b60a084015250612f80565b81602001516001600160a01b031663a95b089f8784604001518803876040518463ffffffff1660e01b8152600401808460ff1681526020018360ff168152602001828152602001935050505060206040518083038186803b158015612f4a57600080fd5b505afa158015612f5e573d6000803e3d6000fd5b505050506040513d6020811015612f7457600080fd5b505192506113ff915050565b816040015195505b604082015160ff908116608084018190529086161015612fac5760ff851660808301525b6000612fcb612fba8a61367c565b8885608001518660a0015186613711565b9050612fe96001610b658385876080015160ff1681518110610b4f57fe5b60c0840181905260048a015460009161300c916402540be40091610afe91613562565b60c085015190915061301e9082613411565b60c08501525050604082015160ff90811690861610156130795761306f88600801836080015160ff168154811061305157fe5b90600052602060002001548360c0015161361590919063ffffffff16565b60c083015261312a565b81602001516001600160a01b031663342a87a16130b38460000151610afe670de0b6b3a76400008760c0015161356290919063ffffffff16565b846040015188036040518363ffffffff1660e01b8152600401808381526020018260ff1681526020019250505060206040518083038186803b1580156130f857600080fd5b505afa15801561310c573d6000803e3d6000fd5b505050506040513d602081101561312257600080fd5b505160c08301525b5060c001519695505050505050565b6000806132046131f68560090180548060200260200160405190810160405280929190818152602001828054801561319057602002820191906000526020600020905b81548152602001906001019080831161317c575b5050505050866008018054806020026020016040519081016040528092919081815260200182805480156131e357602002820191906000526020600020905b8154815260200190600101908083116131cf575b50505050506131f1876139dc565b6134fc565b6131ff8661367c565b613c4e565b905060008460060160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561325857600080fd5b505afa15801561326c573d6000803e3d6000fd5b505050506040513d602081101561328257600080fd5b5051905080156132ab576132a281610afe84670de0b6b3a7640000613562565b925050506132b2565b6000925050505b92915050565b600061334a8584846132c9886139dc565b8960060160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561331957600080fd5b505afa15801561332d573d6000803e3d6000fd5b505050506040513d602081101561334357600080fd5b5051613e82565b5095945050505050565b6000610258826002015401421115613405578154604080517fe25aa5fa00000000000000000000000000000000000000000000000000000000815290516000926001600160a01b03169163e25aa5fa916004808301926020929190829003018186803b1580156133c357600080fd5b505afa1580156133d7573d6000803e3d6000fd5b505050506040513d60208110156133ed57600080fd5b505160018401819055426002850155915061340c9050565b5060018101545b919050565b600082821115613468576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd000000000000000000000000000000000000000000000000000000001790526134f6908590614006565b50505050565b60608061350985856140b7565b905060006135226001875161341190919063ffffffff16565b9050613540670de0b6b3a7640000610afe8685858151811061185a57fe5b82828151811061354c57fe5b60209081029190910101525090505b9392505050565b600082613571575060006132b2565b8282028284828161357e57fe5b041461355b5760405162461bcd60e51b8152600401808060200182810382526021815260200180614a9f6021913960400191505060405180910390fd5b60008282018381101561355b576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600080821161366b576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161367457fe5b049392505050565b6003810154600182015460009190428211156137085760028401548454808311156136da576136cf6136c86136b18685613411565b610afe6136be4287613411565b6113dc8887613411565b82906135bb565b94505050505061340c565b6136cf6137016136ea8685613411565b610afe6136f74287613411565b6113dc8689613411565b8290613411565b915061340c9050565b805160009060ff8681169086161415613771576040805162461bcd60e51b815260206004820152601d60248201527f43616e277420636f6d7061726520746f6b656e20746f20697473656c66000000604482015290519081900360640190fd5b808660ff161080156137855750808560ff16105b6137d6576040805162461bcd60e51b815260206004820152601660248201527f546f6b656e73206d75737420626520696e20706f6f6c00000000000000000000604482015290519081900360640190fd5b60006137e28489613c4e565b9050806000806137f2858c613562565b90506000805b8681101561386b578b60ff168114156138135789915061383d565b8a60ff1681146138385788818151811061382957fe5b6020026020010151915061383d565b613863565b61384784836135bb565b93506138606138568389613562565b610afe8789613562565b94505b6001016137f8565b506138886138798388613562565b610afe60646113dc888a613562565b935060006138a561389e84610afe896064613562565b85906135bb565b9050600086815b6101008110156139095790915081906138df6138d18a610b6587610b04876002613562565b610afe8a610b048680613562565b91506138eb82846141ae565b15613901575098506113ff975050505050505050565b6001016138ac565b506040805162461bcd60e51b815260206004820152601e60248201527f417070726f78696d6174696f6e20646964206e6f7420636f6e76657267650000604482015290519081900360640190fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001790526139d7908490614006565b505050565b6000610258826002015401421115613405578154604080517fe25aa5fa00000000000000000000000000000000000000000000000000000000815290516001600160a01b039092169163e25aa5fa91600480820192602092909190829003018186803b158015613a4b57600080fd5b505afa158015613a5f573d6000803e3d6000fd5b505050506040513d6020811015613a7557600080fd5b5051905061340c565b6000806060613a8d8885613ee5565b905080518760ff16108015613aa5575080518660ff16105b613af6576040805162461bcd60e51b815260206004820152601860248201527f546f6b656e20696e646578206f7574206f662072616e67650000000000000000604482015290519081900360640190fd5b8051600090613b06906001613411565b90506000613b398a6008018a60ff1681548110613b1f57fe5b90600052602060002001548861356290919063ffffffff16565b9050818960ff161415613b5f57613b5c670de0b6b3a7640000610afe8389613562565b90505b613b88838a60ff1681518110613b7157fe5b6020026020010151826135bb90919063ffffffff16565b90506000613ba1613b988c61367c565b8b8b8588613711565b9050613bbb6001610b6583878d60ff1681518110610b4f57fe5b9550828960ff161415613be157613bde87610afe88670de0b6b3a7640000613562565b95505b613c016402540be400610afe8d600401548961356290919063ffffffff16565b9450613c0d8686613411565b9550613c3e8b6008018a60ff1681548110613c2457fe5b90600052602060002001548761361590919063ffffffff16565b9550505050509550959350505050565b815160009081805b82811015613c8e57613c84868281518110613c6d57fe5b6020026020010151836135bb90919063ffffffff16565b9150600101613c56565b5080613c9f576000925050506132b2565b60008181613cad8786613562565b905060005b610100811015613d66578260005b87811015613cef57613ce5613cdb898d848151811061185a57fe5b610afe8488613562565b9150600101613cc0565b509293508392613d3d613d1d613d0a836113dc8b60016135bb565b610b046064610afe896113dc8a84613411565b610afe866113dc613d2e868d613562565b610b046064610afe8b8f613562565b9350613d4984866141ae565b15613d5d57839750505050505050506132b2565b50600101613cb2565b506040805162461bcd60e51b815260206004820152601360248201527f4420646f6573206e6f7420636f6e766572676500000000000000000000000000604482015290519081900360640190fd5b60008184841115613e435760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015613e08578181015183820152602001613df0565b50505050905090810190601f168015613e355780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600061355b613e6060046113dc856001613411565b610afe8585613562565b600081831115613e7d57508082036132b2565b500390565b600080600080600080613e988b8a8c8b8b6141c5565b809450819350829650505050613ed384610b658d6008018c60ff1681548110613ebd57fe5b600091825260209091200154610afe8686613411565b939b939a509298505050505050505050565b606061355b83600901805480602002602001604051908101604052809291908181526020018280548015613f3857602002820191906000526020600020905b815481526020019060010190808311613f24575b505050505084600801805480602002602001604051908101604052809291908181526020018280548015613f8b57602002820191906000526020600020905b815481526020019060010190808311613f77575b5050505050846134fc565b6000816001600160a01b0316635fd65f0f6040518163ffffffff1660e01b815260040160e06040518083038186803b158015613fd157600080fd5b505afa158015613fe5573d6000803e3d6000fd5b505050506040513d60e0811015613ffb57600080fd5b506080015192915050565b606061405b826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661457d9092919063ffffffff16565b8051909150156139d75780806020019051602081101561407a57600080fd5b50516139d75760405162461bcd60e51b815260040180806020018281038252602a815260200180614ac0602a913960400191505060405180910390fd5b81518151606091908114614112576040805162461bcd60e51b815260206004820152601f60248201527f42616c616e636573206d757374206d61746368206d756c7469706c6965727300604482015290519081900360640190fd5b60608167ffffffffffffffff8111801561412b57600080fd5b50604051908082528060200260200182016040528015614155578160200160208202803683370190505b50905060005b828110156141a55761418685828151811061417257fe5b602002602001015187838151811061185a57fe5b82828151811061419257fe5b602090810291909101015260010161415b565b50949350505050565b600060016141bc8484613e6a565b11159392505050565b600080600060606141d68987613ee5565b905080518860ff1610614230576040805162461bcd60e51b815260206004820152601860248201527f546f6b656e20696e646578206f7574206f662072616e67650000000000000000604482015290519081900360640190fd5b614238614a2d565b6040518060c00160405280600081526020016000815260200160008152602001600081526020016142688c61367c565b815260200160008152509050614282828260800151613c4e565b8082526142a29061429a908890610afe908c90613562565b825190613411565b60208201528151829060ff8b169081106142b857fe5b6020026020010151881115614314576040805162461bcd60e51b815260206004820152601a60248201527f5769746864726177206578636565647320617661696c61626c65000000000000604482015290519081900360640190fd5b61432881608001518a84846020015161458c565b6040820152815160609067ffffffffffffffff8111801561434857600080fd5b50604051908082528060200260200182016040528015614372578160200160208202803683370190505b5090506143848b600401548451613e4b565b606083015260005b835181101561445b578381815181106143a157fe5b60200260200101518360a001818152505061443c6144316402540be400610afe86606001518f60ff168614614406576144016143f68960000151610afe8b602001518c60a0015161356290919063ffffffff16565b60a08a015190613411565b6113dc565b6113dc8860400151610b658a60000151610afe8c602001518d60a0015161356290919063ffffffff16565b60a085015190613411565b82828151811061444857fe5b602090810291909101015260010161438c565b50600061448461447584608001518d85876020015161458c565b838d60ff1681518110610b4f57fe5b8451909150614494906001613411565b8b60ff16141561451d576144b489610afe83670de0b6b3a7640000613562565b90506144d989610afe670de0b6b3a7640000866040015161356290919063ffffffff16565b83604001818152505061450189610afe670de0b6b3a7640000878f60ff168151811061185a57fe5b848c60ff168151811061451057fe5b6020026020010181815250505b6145498c6008018c60ff168154811061453257fe5b600091825260209091200154610afe836001613411565b9050808360400151858d60ff168151811061456057fe5b602002602001015196509650965050505050955095509592505050565b6060611b9584846000856146f9565b815160009060ff851681116145e8576040805162461bcd60e51b815260206004820152600f60248201527f546f6b656e206e6f7420666f756e640000000000000000000000000000000000604482015290519081900360640190fd5b826000806145f68985613562565b905060005b8481101561465d578860ff1681146146555761463388828151811061461c57fe5b6020026020010151846135bb90919063ffffffff16565b9250614652614648868a848151811061185a57fe5b610afe868a613562565b93505b6001016145fb565b5061467a61466b8286613562565b610afe60646113dc878b613562565b9250600061469761469083610afe8a6064613562565b84906135bb565b9050600087815b6101008110156139095790915081906146d16146c38b610b6587610b04876002613562565b610afe89610b048680613562565b91506146dd82846141ae565b156146f157509650611b9595505050505050565b60010161469e565b60608247101561473a5760405162461bcd60e51b8152600401808060200182810382526026815260200180614a796026913960400191505060405180910390fd5b61474385614873565b614794576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b602083106147f157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016147b4565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114614853576040519150601f19603f3d011682016040523d82523d6000602084013e614858565b606091505b5091509150614868828286614879565b979650505050505050565b3b151590565b6060831561488857508161355b565b8251156148985782518084602001fd5b60405162461bcd60e51b8152602060048201818152845160248401528451859391928392604401919085019080838360008315613e08578181015183820152602001613df0565b60405180610160016040528060008152602001600081526020016000815260200160608152602001606081526020016060815260200160006001600160a01b03168152602001600060ff16815260200160006001600160a01b03168152602001600060ff168152602001600081525090565b60405180610120016040528060008152602001600081526020016000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160608152602001606081525090565b8280548282559060005260206000209081019282156149e1579160200282015b828111156149e15782518255916020019190600101906149c6565b506149ed929150614a63565b5090565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b5b808211156149ed5760008155600101614a6456fe416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656443616e6e6f74207769746864726177206d6f7265207468616e20617661696c61626c65a26469706673582212205f3f2a35d33799d1aebf3633885c13817512fbe508a276b18516db4cf2cc3f6b64736f6c634300060c0033
Deployed Bytecode
0x733165e09ca44b52b4dc40eab8d867a39910c1d53f30146080604052600436106100dd5760003560e01c8063becf967c1161008b578063d45757ba11610065578063d45757ba1461041d578063e9d4d07714610459578063f6ada8c21461047c576100dd565b8063becf967c14610312578063c1a662731461031a578063d1ab4b4a1461035f576100dd565b80637e971656116100bc5780637e971656146101fd57806398414eed146102bb578063a28c23411461030a576100dd565b806280247a146100e25780635afb90ff1461014357806378b0bcbe1461017f575b600080fd5b8180156100ee57600080fd5b50610131600480360360c081101561010557600080fd5b5080359060208101359060ff604082013581169160608101359091169060808101359060a001356104ae565b60408051918252519081900360200190f35b610131600480360360a081101561015957600080fd5b5080359060208101359060ff604082013581169160608101359091169060800135611162565b6101316004803603608081101561019557600080fd5b8135916020810135918101906060810160408201356401000000008111156101bc57600080fd5b8201836020820111156101ce57600080fd5b803590602001918460208302840111640100000000831117156101f057600080fd5b9193509150351515611184565b81801561020957600080fd5b506101316004803603608081101561022057600080fd5b81359160208101359181019060608101604082013564010000000081111561024757600080fd5b82018360208201111561025957600080fd5b8035906020019184602083028401116401000000008311171561027b57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250611408915050565b8180156102c757600080fd5b50610131600480360360c08110156102de57600080fd5b5080359060208101359060ff604082013581169160608101359091169060808101359060a00135611b9d565b610131611fb7565b610131611fc3565b81801561032657600080fd5b50610131600480360360a081101561033d57600080fd5b5080359060208101359060408101359060ff6060820135169060800135611fc9565b81801561036b57600080fd5b506101316004803603608081101561038257600080fd5b8135916020810135918101906060810160408201356401000000008111156103a957600080fd5b8201836020820111156103bb57600080fd5b803590602001918460208302840111640100000000831117156103dd57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250612355915050565b610131600480360360a081101561043357600080fd5b5080359060208101359060ff604082013581169160608101359091169060800135612bb6565b6101316004803603604081101561046f57600080fd5b5080359060200135613139565b6101316004803603608081101561049257600080fd5b508035906020810135906040810135906060013560ff166132b8565b60006104b86148df565b6040518061016001604052806000815260200160008152602001600081526020018960080180548060200260200160405190810160405280929190818152602001828054801561052757602002820191906000526020600020905b815481526020019060010190808311610513575b505050505081526020018960090180548060200260200160405190810160405280929190818152602001828054801561057f57602002820191906000526020600020905b81548152602001906001019080831161056b575b50505050508152602001886003018054806020026020016040519081016040528092919081815260200182805480156105e157602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116105c3575b50505091835250506000602082018190526040820181905260608201819052608082015260a00161061189613354565b905260808101515190915060009061062a906001613411565b905060008260a00151518260ff160190508060ff168860ff1610801561065557508060ff168760ff16105b6106a6576040805162461bcd60e51b815260206004820152601860248201527f546f6b656e20696e646578206f7574206f662072616e67650000000000000000604482015290519081900360640190fd5b5087546001600160a01b031660ff80831690891610156106fa57896007018860ff16815481106106d257fe5b6000918252602090912001546001600160a01b031660c084015260ff881660e0840152610732565b8260a0015182890360ff168151811061070f57fe5b60209081029190910101516001600160a01b031660c084015260ff821660e08401525b8160ff168760ff16101561077c57896007018760ff168154811061075257fe5b6000918252602090912001546001600160a01b031661010084015260ff87166101208401526107b6565b8260a0015182880360ff168151811061079157fe5b60209081029190910101516001600160a01b031661010084015260ff82166101208401525b8260c001516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561080757600080fd5b505afa15801561081b573d6000803e3d6000fd5b505050506040513d602081101561083157600080fd5b5051602084015260c0830151610852906001600160a01b031633308961346e565b6108dd83602001518460c001516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156108ab57600080fd5b505afa1580156108bf573d6000803e3d6000fd5b505050506040513d60208110156108d557600080fd5b505190613411565b602084015260ff80831690891610806108fb57508160ff168760ff16105b15610f2b57606061091a846080015185606001518661014001516134fc565b90508260ff168960ff1610156109865761097f61095a85606001518b60ff168151811061094357fe5b60200260200101518961356290919063ffffffff16565b828b60ff168151811061096957fe5b60200260200101516135bb90919063ffffffff16565b8452610b0e565b60608460a001515167ffffffffffffffff811180156109a457600080fd5b506040519080825280602002602001820160405280156109ce578160200160208202803683370190505b509050846020015181858c0360ff16815181106109e757fe5b602002602001018181525050826001600160a01b0316634d49e87d826000426040518463ffffffff1660e01b81526004018080602001848152602001838152602001828103825285818151815260200191508051906020019060200280838360005b83811015610a61578181015183820152602001610a49565b50505050905001945050505050602060405180830381600087803b158015610a8857600080fd5b505af1158015610a9c573d6000803e3d6000fd5b505050506040513d6020811015610ab257600080fd5b505160208601528151610b0a90839060ff8716908110610ace57fe5b6020026020010151610b04670de0b6b3a7640000610afe8961014001518a6020015161356290919063ffffffff16565b90613615565b906135bb565b8552505b600080610b30610b1d8e61367c565b60e0880151610120890151895187613711565b9050610b6b6001610b6583868a610120015160ff1681518110610b4f57fe5b602002602001015161341190919063ffffffff16565b90613411565b604087015260ff808616908b1610610bab57610ba5866101400151610afe670de0b6b3a7640000896040015161356290919063ffffffff16565b60408701525b610bcf6402540be400610afe8f60040154896040015161356290919063ffffffff16565b9150610c0b866060015187610120015160ff1681518110610bec57fe5b6020026020010151610afe84896040015161341190919063ffffffff16565b60408701525060058c0154600090610c2f906402540be40090610afe908590613562565b9050610c63866060015187610120015160ff1681518110610c4c57fe5b60200260200101518261361590919063ffffffff16565b9050610c84866020015187608001518860e0015160ff168151811061096957fe5b8d6009018760e0015160ff1681548110610c9a57fe5b9060005260206000200181905550610ccc81610b65886040015189608001518a610120015160ff1681518110610b4f57fe5b8d60090187610120015160ff1681548110610ce357fe5b9060005260206000200181905550508360ff168960ff1610610ecb5760008561010001516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610d5357600080fd5b505afa158015610d67573d6000803e3d6000fd5b505050506040513d6020811015610d7d57600080fd5b505160408088015181517f3e3a1560000000000000000000000000000000000000000000000000000000008152600481019190915260ff888e0316602482015260006044820181905242606483015291519293506001600160a01b03871692633e3a156092608480840193602093929083900390910190829087803b158015610e0557600080fd5b505af1158015610e19573d6000803e3d6000fd5b505050506040513d6020811015610e2f57600080fd5b5050610100860151604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905183926001600160a01b0316916370a08231916024808301926020929190829003018186803b158015610e9757600080fd5b505afa158015610eab573d6000803e3d6000fd5b505050506040513d6020811015610ec157600080fd5b5051036040870152505b8685604001511015610f24576040805162461bcd60e51b815260206004820181905260248201527f53776170206469646e277420726573756c7420696e206d696e20746f6b656e73604482015290519081900360640190fd5b50506110d8565b8261010001516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610f7d57600080fd5b505afa158015610f91573d6000803e3d6000fd5b505050506040513d6020811015610fa757600080fd5b505160408085019190915260208481015182517f9169558600000000000000000000000000000000000000000000000000000000815260ff868d0381166004830152868c0316602482015260448101919091526064810188905242608482015291516001600160a01b0384169263916955869260a48083019391928290030181600087803b15801561103857600080fd5b505af115801561104c573d6000803e3d6000fd5b505050506040513d602081101561106257600080fd5b505060408084015161010085015182517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015292516110d2936001600160a01b03909216916370a08231916024808301926020929190829003018186803b1580156108ab57600080fd5b60408401525b6110ff3384604001518561010001516001600160a01b03166139579092919063ffffffff16565b6040808401518151888152602081019190915260ff808b168284015289166060820152905133917f6617207207e397b41fc98016d8c9febb7223f44c355db66ad429730f2b950a60919081900360800190a2505060400151979650505050505050565b6000611179868585856111748a6139dc565b613a7e565b509695505050505050565b6000806111908761367c565b905060008060006111a0896139dc565b905060608a6009018054806020026020016040519081016040528092919081815260200182805480156111f257602002820191906000526020600020905b8154815260200190600101908083116111de575b5050505050905060608b60080180548060200260200160405190810160405280929190818152602001828054801561124957602002820191906000526020600020905b815481526020019060010190808311611235575b5050505050905060008251905061126a6112648484876134fc565b88613c4e565b955060005b8181101561132f5789156112bc5761129f8c8c8381811061128c57fe5b9050602002013585838151811061096957fe5b8482815181106112ab57fe5b602002602001018181525050611327565b61130e8c8c838181106112cb57fe5b90506020020135604051806060016040528060238152602001614aea602391398684815181106112f757fe5b6020026020010151613db49092919063ffffffff16565b84828151811061131a57fe5b6020026020010181815250505b60010161126f565b5061133e6112648484876134fc565b94505050505060008960060160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561139657600080fd5b505afa1580156113aa573d6000803e3d6000fd5b505050506040513d60208110156113c057600080fd5b5051905085156113ed576113e283610afe836113dc8684613411565b90613562565b9450505050506113ff565b6113e283610afe836113dc8387613411565b95945050505050565b6000611412614951565b604080516101208101825260008082526020820181905291810182905260068801546001600160a01b03166060820152608081019190915260a081016114578861367c565b815260200161146587613354565b8152602001876008018054806020026020016040519081016040528092919081815260200182805480156114b857602002820191906000526020600020905b8154815260200190600101908083116114a4575b505050505081526020018760090180548060200260200160405190810160405280929190818152602001828054801561151057602002820191906000526020600020905b8154815260200190600101908083116114fc575b5050505050815250905080606001516001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561155757600080fd5b505afa15801561156b573d6000803e3d6000fd5b505050506040513d602081101561158157600080fd5b50516080820152610100810151518451146115e3576040805162461bcd60e51b815260206004820181905260248201527f416d6f756e74732073686f756c64206d6174636820706f6f6c20746f6b656e73604482015290519081900360640190fd5b82611635576040805162461bcd60e51b815260206004820152601560248201527f4d757374206275726e206d6f7265207468616e20300000000000000000000000604482015290519081900360640190fd5b600061164b876004015483610100015151613e4b565b905060608261010001515167ffffffffffffffff8111801561166c57600080fd5b50604051908082528060200260200182016040528015611696578160200160208202803683370190505b50905060608361010001515167ffffffffffffffff811180156116b857600080fd5b506040519080825280602002602001820160405280156116e2578160200160208202803683370190505b50905061170a6117008561010001518660e001518760c001516134fc565b8560a00151613c4e565b845260005b8461010001515181101561177d5761175e88828151811061172c57fe5b6020026020010151604051806060016040528060238152602001614aea6023913987610100015184815181106112f757fe5b82828151811061176a57fe5b602090810291909101015260010161170f565b50611794611700828660e001518760c001516134fc565b602085015260005b846101000151518110156118dd5760006117e38660000151610afe88610100015185815181106117c857fe5b6020026020010151896020015161356290919063ffffffff16565b9050600061180d8484815181106117f657fe5b602002602001015183613e6a90919063ffffffff16565b90506118226402540be400610afe8884613562565b85848151811061182e57fe5b60200260200101818152505061187c6118706402540be400610afe8f6005015489888151811061185a57fe5b602002602001015161356290919063ffffffff16565b858581518110610b4f57fe5b8c600901848154811061188b57fe5b90600052602060002001819055506118bc8584815181106118a857fe5b6020026020010151858581518110610b4f57fe5b8484815181106118c857fe5b6020908102919091010152505060010161179c565b506118f4611700828660e001518760c001516134fc565b60408501819052845160808601516000935061191a92610afe91906113dc908490613411565b90508061196e576040805162461bcd60e51b815260206004820152601b60248201527f4275726e7420616d6f756e742063616e6e6f74206265207a65726f0000000000604482015290519081900360640190fd5b6119798160016135bb565b9050858111156119d0576040805162461bcd60e51b815260206004820152601b60248201527f746f6b656e416d6f756e74203e206d61784275726e416d6f756e740000000000604482015290519081900360640190fd5b83606001516001600160a01b03166379cc679033836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015611a2b57600080fd5b505af1158015611a3f573d6000803e3d6000fd5b5050505060005b84610100015151811015611a9f57611a9733898381518110611a6457fe5b60200260200101518c6007018481548110611a7b57fe5b6000918252602090912001546001600160a01b03169190613957565b600101611a46565b50336001600160a01b03167f3631c28b1f9dd213e0319fb167b554d76b6c283a41143eb400a0d1adb1af175588848760200151611ae9868a6080015161341190919063ffffffff16565b604051808060200180602001858152602001848152602001838103835287818151815260200191508051906020019060200280838360005b83811015611b39578181015183820152602001611b21565b50505050905001838103825286818151815260200191508051906020019060200280838360005b83811015611b78578181015183820152602001611b60565b50505050905001965050505050505060405180910390a293505050505b949350505050565b600786015460009060ff861681118015611bb95750808560ff16105b611c0a576040805162461bcd60e51b815260206004820152601b60248201527f546f6b656e20696e646578206973206f7574206f662072616e67650000000000604482015290519081900360640190fd5b50600080886007018760ff1681548110611c2057fe5b60009182526020918290200154604080517f70a0823100000000000000000000000000000000000000000000000000000000815233600482015290516001600160a01b03909216935083926370a0823192602480840193829003018186803b158015611c8b57600080fd5b505afa158015611c9f573d6000803e3d6000fd5b505050506040513d6020811015611cb557600080fd5b5051851115611d0b576040805162461bcd60e51b815260206004820152601d60248201527f43616e6e6f742073776170206d6f7265207468616e20796f75206f776e000000604482015290519081900360640190fd5b6000816001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015611d5a57600080fd5b505afa158015611d6e573d6000803e3d6000fd5b505050506040513d6020811015611d8457600080fd5b50519050611d9d6001600160a01b03831633308961346e565b611dee81836001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156108ab57600080fd5b92505050600080611e058a8989866111748e613354565b9150915084821015611e5e576040805162461bcd60e51b815260206004820181905260248201527f53776170206469646e277420726573756c7420696e206d696e20746f6b656e73604482015290519081900360640190fd5b6000611ea08b6008018960ff1681548110611e7557fe5b9060005260206000200154610afe6402540be400610afe8f600501548761356290919063ffffffff16565b9050611ed1848c6009018b60ff1681548110611eb857fe5b90600052602060002001546135bb90919063ffffffff16565b8b6009018a60ff1681548110611ee357fe5b9060005260206000200181905550611f2481610b65858e6009018c60ff1681548110611f0b57fe5b906000526020600020015461341190919063ffffffff16565b8b6009018960ff1681548110611f3657fe5b9060005260206000200181905550611f5b33848d6007018b60ff1681548110611a7b57fe5b604080518581526020810185905260ff808c16828401528a166060820152905133917fc6c1e0630dbe9130cc068028486c0d118ddcea348550819defd5cb8c257f8a38919081900360800190a250909998505050505050505050565b670de0b6b3a764000081565b61025881565b6006850154604080517f18160ddd00000000000000000000000000000000000000000000000000000000815290516000926001600160a01b031691839183916318160ddd916004808301926020929190829003018186803b15801561202d57600080fd5b505afa158015612041573d6000803e3d6000fd5b505050506040513d602081101561205757600080fd5b50516007890154604080517f70a08231000000000000000000000000000000000000000000000000000000008152336004820152905192935090916001600160a01b038516916370a08231916024808301926020929190829003018186803b1580156120c257600080fd5b505afa1580156120d6573d6000803e3d6000fd5b505050506040513d60208110156120ec57600080fd5b5051871115612142576040805162461bcd60e51b815260206004820152600d60248201527f3e4c502e62616c616e63654f6600000000000000000000000000000000000000604482015290519081900360640190fd5b808660ff1610612199576040805162461bcd60e51b815260206004820152600f60248201527f546f6b656e206e6f7420666f756e640000000000000000000000000000000000604482015290519081900360640190fd5b6000806121b18b8a8a6121ab8e613354565b88613e82565b925090508681101561220a576040805162461bcd60e51b815260206004820152600e60248201527f6479203c206d696e416d6f756e74000000000000000000000000000000000000604482015290519081900360640190fd5b6122496122376122306402540be400610afe8f600501548761356290919063ffffffff16565b83906135bb565b8c6009018a60ff1681548110611f0b57fe5b8b6009018960ff168154811061225b57fe5b6000918252602082200191909155604080517f79cc6790000000000000000000000000000000000000000000000000000000008152336004820152602481018c905290516001600160a01b038816926379cc6790926044808201939182900301818387803b1580156122cc57600080fd5b505af11580156122e0573d6000803e3d6000fd5b505050506122fb33828d6007018b60ff1681548110611a7b57fe5b604080518a81526020810186905260ff8a168183015260608101839052905133917f43fb02998f4e03da2e0e6fff53fdbf0c40a9f45f145dc377fc30615d7d7a8a64919081900360800190a29a9950505050505050505050565b60006060856007018054806020026020016040519081016040528092919081815260200182805480156123b157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612393575b50505050509050805184511461240e576040805162461bcd60e51b815260206004820181905260248201527f416d6f756e7473206d757374206d6174636820706f6f6c656420746f6b656e73604482015290519081900360640190fd5b6060815167ffffffffffffffff8111801561242857600080fd5b50604051908082528060200260200182016040528015612452578160200160208202803683370190505b50905061245d614951565b604080516101208101825260008082526020820181905291810182905260068a01546001600160a01b03166060820152608081019190915260a081016124a28a61367c565b81526020016124b089613354565b81526020018960080180548060200260200160405190810160405280929190818152602001828054801561250357602002820191906000526020600020905b8154815260200190600101908083116124ef575b505050505081526020018960090180548060200260200160405190810160405280929190818152602001828054801561255b57602002820191906000526020600020905b815481526020019060010190808311612547575b5050505050815250905080606001516001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156125a257600080fd5b505afa1580156125b6573d6000803e3d6000fd5b505050506040513d60208110156125cc57600080fd5b50516080820181905215612602576125ff6125f58261010001518360e001518460c001516134fc565b8260a00151613c4e565b81525b60005b83518110156128455760808201511515806126335750600087828151811061262957fe5b6020026020010151115b612684576040805162461bcd60e51b815260206004820152601e60248201527f4d75737420737570706c7920616c6c20746f6b656e7320696e20706f6f6c0000604482015290519081900360640190fd5b86818151811061269057fe5b60200260200101516000146127f95760008482815181106126ad57fe5b60200260200101516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561270157600080fd5b505afa158015612715573d6000803e3d6000fd5b505050506040513d602081101561272b57600080fd5b5051885190915061277b90339030908b908690811061274657fe5b602002602001015188868151811061275a57fe5b60200260200101516001600160a01b031661346e909392919063ffffffff16565b6127df8186848151811061278b57fe5b60200260200101516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156108ab57600080fd5b8883815181106127eb57fe5b602002602001018181525050505b61282187828151811061280857fe5b6020026020010151836101000151838151811061096957fe5b826101000151828151811061283257fe5b6020908102919091010152600101612605565b506128616125f58261010001518360e001518460c001516134fc565b602082018190528151106128bc576040805162461bcd60e51b815260206004820152601160248201527f442073686f756c6420696e637265617365000000000000000000000000000000604482015290519081900360640190fd5b60208101516040820152608081015160009015612a6e5760006128e48a600401548651613e4b565b905060005b8551811015612a1f57600061292c8560000151610afe8e600901858154811061290e57fe5b9060005260206000200154886020015161356290919063ffffffff16565b905061296c6402540be400610afe612965886101000151868151811061294e57fe5b602002602001015185613e6a90919063ffffffff16565b8690613562565b86838151811061297857fe5b6020026020010181815250506129b56129a46402540be400610afe8f600501548a878151811061185a57fe5b8661010001518481518110610b4f57fe5b8c60090183815481106129c457fe5b90600052602060002001819055506129fa8683815181106129e157fe5b60200260200101518661010001518481518110610b4f57fe5b8561010001518381518110612a0b57fe5b6020908102919091010152506001016128e9565b50612a45612a3b8461010001518560e001518660c001516134fc565b8460a00151613c4e565b6040840181905283516080850151612a6692610afe91906113dc9084613411565b915050612a92565b6101008201518051612a8a9160098c01916020909101906149a6565b505060208101515b85811015612ae7576040805162461bcd60e51b815260206004820152601b60248201527f436f756c646e2774206d696e74206d696e207265717565737465640000000000604482015290519081900360640190fd5b6006890154604080517f40c10f190000000000000000000000000000000000000000000000000000000081523360048201526024810184905290516001600160a01b03909216916340c10f199160448082019260009290919082900301818387803b158015612b5557600080fd5b505af1158015612b69573d6000803e3d6000fd5b50505050336001600160a01b03167f189c623b666b1b45b83d7178f39b8c087cb09774317ca2f53c2d3c3726f222a288858560200151611ae98688608001516135bb90919063ffffffff16565b6000612bc06149f1565b6040518060e00160405280612bd4886139dc565b815287546001600160a01b03166020820152600060408201819052600389015460ff166060808401919091526080830182905260a0830182905260c090920152815191925090612c25908990613ee5565b8051909150612c35906001613411565b60ff9081166040840181905260608401510190818116908816108015612c6057508060ff168660ff16105b612cb1576040805162461bcd60e51b815260206004820152601860248201527f546f6b656e20696e646578206f7574206f662072616e67650000000000000000604482015290519081900360640190fd5b50816040015160ff168660ff161015612d0f57612d05612cf6896008018860ff1681548110612cdc57fe5b90600052602060002001548661356290919063ffffffff16565b828860ff168151811061096957fe5b60a0830152612f88565b6040820151958690039560ff9081169086161015612ee6576060826060015160ff1667ffffffffffffffff81118015612d4757600080fd5b50604051908082528060200260200182016040528015612d71578160200160208202803683370190505b50905084818860ff1681518110612d8457fe5b602002602001018181525050612e67670de0b6b3a7640000610afe856000015186602001516001600160a01b031663e6ab28068660016040518363ffffffff1660e01b815260040180806020018315158152602001828103825284818151815260200191508051906020019060200280838360005b83811015612e11578181015183820152602001612df9565b50505050905001935050505060206040518083038186803b158015612e3557600080fd5b505afa158015612e49573d6000803e3d6000fd5b505050506040513d6020811015612e5f57600080fd5b505190613562565b8360a0018181525050612edb82846040015160ff1681518110612e8657fe5b6020026020010151610b04612ed0612ead60026402540be40061356290919063ffffffff16565b8c54610afe90612ec5906001600160a01b0316613f96565b60a08a015190613562565b60a087015190613411565b60a084015250612f80565b81602001516001600160a01b031663a95b089f8784604001518803876040518463ffffffff1660e01b8152600401808460ff1681526020018360ff168152602001828152602001935050505060206040518083038186803b158015612f4a57600080fd5b505afa158015612f5e573d6000803e3d6000fd5b505050506040513d6020811015612f7457600080fd5b505192506113ff915050565b816040015195505b604082015160ff908116608084018190529086161015612fac5760ff851660808301525b6000612fcb612fba8a61367c565b8885608001518660a0015186613711565b9050612fe96001610b658385876080015160ff1681518110610b4f57fe5b60c0840181905260048a015460009161300c916402540be40091610afe91613562565b60c085015190915061301e9082613411565b60c08501525050604082015160ff90811690861610156130795761306f88600801836080015160ff168154811061305157fe5b90600052602060002001548360c0015161361590919063ffffffff16565b60c083015261312a565b81602001516001600160a01b031663342a87a16130b38460000151610afe670de0b6b3a76400008760c0015161356290919063ffffffff16565b846040015188036040518363ffffffff1660e01b8152600401808381526020018260ff1681526020019250505060206040518083038186803b1580156130f857600080fd5b505afa15801561310c573d6000803e3d6000fd5b505050506040513d602081101561312257600080fd5b505160c08301525b5060c001519695505050505050565b6000806132046131f68560090180548060200260200160405190810160405280929190818152602001828054801561319057602002820191906000526020600020905b81548152602001906001019080831161317c575b5050505050866008018054806020026020016040519081016040528092919081815260200182805480156131e357602002820191906000526020600020905b8154815260200190600101908083116131cf575b50505050506131f1876139dc565b6134fc565b6131ff8661367c565b613c4e565b905060008460060160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561325857600080fd5b505afa15801561326c573d6000803e3d6000fd5b505050506040513d602081101561328257600080fd5b5051905080156132ab576132a281610afe84670de0b6b3a7640000613562565b925050506132b2565b6000925050505b92915050565b600061334a8584846132c9886139dc565b8960060160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561331957600080fd5b505afa15801561332d573d6000803e3d6000fd5b505050506040513d602081101561334357600080fd5b5051613e82565b5095945050505050565b6000610258826002015401421115613405578154604080517fe25aa5fa00000000000000000000000000000000000000000000000000000000815290516000926001600160a01b03169163e25aa5fa916004808301926020929190829003018186803b1580156133c357600080fd5b505afa1580156133d7573d6000803e3d6000fd5b505050506040513d60208110156133ed57600080fd5b505160018401819055426002850155915061340c9050565b5060018101545b919050565b600082821115613468576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd000000000000000000000000000000000000000000000000000000001790526134f6908590614006565b50505050565b60608061350985856140b7565b905060006135226001875161341190919063ffffffff16565b9050613540670de0b6b3a7640000610afe8685858151811061185a57fe5b82828151811061354c57fe5b60209081029190910101525090505b9392505050565b600082613571575060006132b2565b8282028284828161357e57fe5b041461355b5760405162461bcd60e51b8152600401808060200182810382526021815260200180614a9f6021913960400191505060405180910390fd5b60008282018381101561355b576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600080821161366b576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161367457fe5b049392505050565b6003810154600182015460009190428211156137085760028401548454808311156136da576136cf6136c86136b18685613411565b610afe6136be4287613411565b6113dc8887613411565b82906135bb565b94505050505061340c565b6136cf6137016136ea8685613411565b610afe6136f74287613411565b6113dc8689613411565b8290613411565b915061340c9050565b805160009060ff8681169086161415613771576040805162461bcd60e51b815260206004820152601d60248201527f43616e277420636f6d7061726520746f6b656e20746f20697473656c66000000604482015290519081900360640190fd5b808660ff161080156137855750808560ff16105b6137d6576040805162461bcd60e51b815260206004820152601660248201527f546f6b656e73206d75737420626520696e20706f6f6c00000000000000000000604482015290519081900360640190fd5b60006137e28489613c4e565b9050806000806137f2858c613562565b90506000805b8681101561386b578b60ff168114156138135789915061383d565b8a60ff1681146138385788818151811061382957fe5b6020026020010151915061383d565b613863565b61384784836135bb565b93506138606138568389613562565b610afe8789613562565b94505b6001016137f8565b506138886138798388613562565b610afe60646113dc888a613562565b935060006138a561389e84610afe896064613562565b85906135bb565b9050600086815b6101008110156139095790915081906138df6138d18a610b6587610b04876002613562565b610afe8a610b048680613562565b91506138eb82846141ae565b15613901575098506113ff975050505050505050565b6001016138ac565b506040805162461bcd60e51b815260206004820152601e60248201527f417070726f78696d6174696f6e20646964206e6f7420636f6e76657267650000604482015290519081900360640190fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001790526139d7908490614006565b505050565b6000610258826002015401421115613405578154604080517fe25aa5fa00000000000000000000000000000000000000000000000000000000815290516001600160a01b039092169163e25aa5fa91600480820192602092909190829003018186803b158015613a4b57600080fd5b505afa158015613a5f573d6000803e3d6000fd5b505050506040513d6020811015613a7557600080fd5b5051905061340c565b6000806060613a8d8885613ee5565b905080518760ff16108015613aa5575080518660ff16105b613af6576040805162461bcd60e51b815260206004820152601860248201527f546f6b656e20696e646578206f7574206f662072616e67650000000000000000604482015290519081900360640190fd5b8051600090613b06906001613411565b90506000613b398a6008018a60ff1681548110613b1f57fe5b90600052602060002001548861356290919063ffffffff16565b9050818960ff161415613b5f57613b5c670de0b6b3a7640000610afe8389613562565b90505b613b88838a60ff1681518110613b7157fe5b6020026020010151826135bb90919063ffffffff16565b90506000613ba1613b988c61367c565b8b8b8588613711565b9050613bbb6001610b6583878d60ff1681518110610b4f57fe5b9550828960ff161415613be157613bde87610afe88670de0b6b3a7640000613562565b95505b613c016402540be400610afe8d600401548961356290919063ffffffff16565b9450613c0d8686613411565b9550613c3e8b6008018a60ff1681548110613c2457fe5b90600052602060002001548761361590919063ffffffff16565b9550505050509550959350505050565b815160009081805b82811015613c8e57613c84868281518110613c6d57fe5b6020026020010151836135bb90919063ffffffff16565b9150600101613c56565b5080613c9f576000925050506132b2565b60008181613cad8786613562565b905060005b610100811015613d66578260005b87811015613cef57613ce5613cdb898d848151811061185a57fe5b610afe8488613562565b9150600101613cc0565b509293508392613d3d613d1d613d0a836113dc8b60016135bb565b610b046064610afe896113dc8a84613411565b610afe866113dc613d2e868d613562565b610b046064610afe8b8f613562565b9350613d4984866141ae565b15613d5d57839750505050505050506132b2565b50600101613cb2565b506040805162461bcd60e51b815260206004820152601360248201527f4420646f6573206e6f7420636f6e766572676500000000000000000000000000604482015290519081900360640190fd5b60008184841115613e435760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015613e08578181015183820152602001613df0565b50505050905090810190601f168015613e355780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600061355b613e6060046113dc856001613411565b610afe8585613562565b600081831115613e7d57508082036132b2565b500390565b600080600080600080613e988b8a8c8b8b6141c5565b809450819350829650505050613ed384610b658d6008018c60ff1681548110613ebd57fe5b600091825260209091200154610afe8686613411565b939b939a509298505050505050505050565b606061355b83600901805480602002602001604051908101604052809291908181526020018280548015613f3857602002820191906000526020600020905b815481526020019060010190808311613f24575b505050505084600801805480602002602001604051908101604052809291908181526020018280548015613f8b57602002820191906000526020600020905b815481526020019060010190808311613f77575b5050505050846134fc565b6000816001600160a01b0316635fd65f0f6040518163ffffffff1660e01b815260040160e06040518083038186803b158015613fd157600080fd5b505afa158015613fe5573d6000803e3d6000fd5b505050506040513d60e0811015613ffb57600080fd5b506080015192915050565b606061405b826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661457d9092919063ffffffff16565b8051909150156139d75780806020019051602081101561407a57600080fd5b50516139d75760405162461bcd60e51b815260040180806020018281038252602a815260200180614ac0602a913960400191505060405180910390fd5b81518151606091908114614112576040805162461bcd60e51b815260206004820152601f60248201527f42616c616e636573206d757374206d61746368206d756c7469706c6965727300604482015290519081900360640190fd5b60608167ffffffffffffffff8111801561412b57600080fd5b50604051908082528060200260200182016040528015614155578160200160208202803683370190505b50905060005b828110156141a55761418685828151811061417257fe5b602002602001015187838151811061185a57fe5b82828151811061419257fe5b602090810291909101015260010161415b565b50949350505050565b600060016141bc8484613e6a565b11159392505050565b600080600060606141d68987613ee5565b905080518860ff1610614230576040805162461bcd60e51b815260206004820152601860248201527f546f6b656e20696e646578206f7574206f662072616e67650000000000000000604482015290519081900360640190fd5b614238614a2d565b6040518060c00160405280600081526020016000815260200160008152602001600081526020016142688c61367c565b815260200160008152509050614282828260800151613c4e565b8082526142a29061429a908890610afe908c90613562565b825190613411565b60208201528151829060ff8b169081106142b857fe5b6020026020010151881115614314576040805162461bcd60e51b815260206004820152601a60248201527f5769746864726177206578636565647320617661696c61626c65000000000000604482015290519081900360640190fd5b61432881608001518a84846020015161458c565b6040820152815160609067ffffffffffffffff8111801561434857600080fd5b50604051908082528060200260200182016040528015614372578160200160208202803683370190505b5090506143848b600401548451613e4b565b606083015260005b835181101561445b578381815181106143a157fe5b60200260200101518360a001818152505061443c6144316402540be400610afe86606001518f60ff168614614406576144016143f68960000151610afe8b602001518c60a0015161356290919063ffffffff16565b60a08a015190613411565b6113dc565b6113dc8860400151610b658a60000151610afe8c602001518d60a0015161356290919063ffffffff16565b60a085015190613411565b82828151811061444857fe5b602090810291909101015260010161438c565b50600061448461447584608001518d85876020015161458c565b838d60ff1681518110610b4f57fe5b8451909150614494906001613411565b8b60ff16141561451d576144b489610afe83670de0b6b3a7640000613562565b90506144d989610afe670de0b6b3a7640000866040015161356290919063ffffffff16565b83604001818152505061450189610afe670de0b6b3a7640000878f60ff168151811061185a57fe5b848c60ff168151811061451057fe5b6020026020010181815250505b6145498c6008018c60ff168154811061453257fe5b600091825260209091200154610afe836001613411565b9050808360400151858d60ff168151811061456057fe5b602002602001015196509650965050505050955095509592505050565b6060611b9584846000856146f9565b815160009060ff851681116145e8576040805162461bcd60e51b815260206004820152600f60248201527f546f6b656e206e6f7420666f756e640000000000000000000000000000000000604482015290519081900360640190fd5b826000806145f68985613562565b905060005b8481101561465d578860ff1681146146555761463388828151811061461c57fe5b6020026020010151846135bb90919063ffffffff16565b9250614652614648868a848151811061185a57fe5b610afe868a613562565b93505b6001016145fb565b5061467a61466b8286613562565b610afe60646113dc878b613562565b9250600061469761469083610afe8a6064613562565b84906135bb565b9050600087815b6101008110156139095790915081906146d16146c38b610b6587610b04876002613562565b610afe89610b048680613562565b91506146dd82846141ae565b156146f157509650611b9595505050505050565b60010161469e565b60608247101561473a5760405162461bcd60e51b8152600401808060200182810382526026815260200180614a796026913960400191505060405180910390fd5b61474385614873565b614794576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b602083106147f157805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016147b4565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114614853576040519150601f19603f3d011682016040523d82523d6000602084013e614858565b606091505b5091509150614868828286614879565b979650505050505050565b3b151590565b6060831561488857508161355b565b8251156148985782518084602001fd5b60405162461bcd60e51b8152602060048201818152845160248401528451859391928392604401919085019080838360008315613e08578181015183820152602001613df0565b60405180610160016040528060008152602001600081526020016000815260200160608152602001606081526020016060815260200160006001600160a01b03168152602001600060ff16815260200160006001600160a01b03168152602001600060ff168152602001600081525090565b60405180610120016040528060008152602001600081526020016000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160608152602001606081525090565b8280548282559060005260206000209081019282156149e1579160200282015b828111156149e15782518255916020019190600101906149c6565b506149ed929150614a63565b5090565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b5b808211156149ed5760008155600101614a6456fe416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656443616e6e6f74207769746864726177206d6f7265207468616e20617661696c61626c65a26469706673582212205f3f2a35d33799d1aebf3633885c13817512fbe508a276b18516db4cf2cc3f6b64736f6c634300060c0033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.