Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 3 internal transactions
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
17135044 | 656 days ago | Contract Creation | 0 ETH | |||
17074539 | 665 days ago | Contract Creation | 0 ETH | |||
17008789 | 674 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
CronV1PoolFactory
Compiler Version
v0.7.6+commit.7338295f
Optimization Enabled:
Yes with 575 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.7.6; import { IERC20 } from "../balancer-core-v2/lib/openzeppelin/IERC20.sol"; import { IVault } from "../balancer-core-v2/vault/interfaces/IVault.sol"; import { BasePoolFactory } from "../balancer-core-v2/pools/factories/BasePoolFactory.sol"; import { ICronV1PoolFactory } from "../interfaces/ICronV1PoolFactory.sol"; import { ICronV1PoolEnums } from "../interfaces/pool/ICronV1PoolEnums.sol"; import { CronV1Pool } from "../CronV1Pool.sol"; import { requireErrCode, CronErrors } from "../miscellany/Errors.sol"; /// @author Cron Finance /// @title Cron V1 Pool Factory contract CronV1PoolFactory is ICronV1PoolFactory, BasePoolFactory { address public override owner; address public override pendingOwner; mapping(address => mapping(address => mapping(uint256 => address))) internal poolMap; /// @notice Only allows the `owner` to execute the function. modifier onlyOwner() { requireErrCode(msg.sender == owner, CronErrors.INVALID_FACTORY_OWNER); _; } /// @notice This function constructs the pool /// @param _vault The balancer v2 vault constructor(IVault _vault) BasePoolFactory(_vault) { owner = msg.sender; emit OwnerChanged(address(0), owner); } /// @notice Deploys a new `CronV1Pool` /// @param _token0 The asset which is converged to ie "base' /// @param _token1 The asset which converges to the underlying /// @param _poolType The type of pool (stable, liquid, volatile) /// @param _name The name of the balancer v2 lp token for this pool /// @param _symbol The symbol of the balancer v2 lp token for this pool /// @return The new pool address function create( address _token0, address _token1, string memory _name, string memory _symbol, uint256 _poolType ) external override(ICronV1PoolFactory) returns (address) { ICronV1PoolEnums.PoolType poolType = ICronV1PoolEnums.PoolType(_poolType); requireErrCode(_token0 != _token1, CronErrors.IDENTICAL_TOKEN_ADDRESSES); (address token0, address token1) = _token0 < _token1 ? (_token0, _token1) : (_token1, _token0); requireErrCode(token0 != address(0), CronErrors.ZERO_TOKEN_ADDRESSES); requireErrCode(poolMap[token0][token1][_poolType] == address(0), CronErrors.EXISTING_POOL); address pool = address(new CronV1Pool(IERC20(token0), IERC20(token1), getVault(), _name, _symbol, poolType)); // Register the pool with the vault _register(pool); // Stores pool information to prevent duplicates poolMap[token0][token1][_poolType] = pool; // Emit a creation event emit CronV1PoolCreated(pool, _token0, _token1, poolType); return pool; } /// @notice Sets `CronV1Pool` address in the mapping /// @param _token0 address of token0 /// @param _token1 address of token1 /// @param _poolType type of pool (stable, liquid, volatile) /// @param _pool address of pool to set in the mapping function set( address _token0, address _token1, uint256 _poolType, address _pool ) external override(ICronV1PoolFactory) onlyOwner { poolMap[_token0][_token1][_poolType] = _pool; ICronV1PoolEnums.PoolType poolType = ICronV1PoolEnums.PoolType(_poolType); emit CronV1PoolSet(_pool, _token0, _token1, poolType); } /// @notice Removes an already deployed `CronV1Pool` from the mapping /// WARNING - Best practice to disable Cron-Fi fees before /// removing it from the factory pool mapping. Also advisable /// to notify LPs / LT swappers in some way that this is /// occurring. /// @param _token0 address of token0 /// @param _token1 address of token1 /// @param _poolType type of pool (stable, liquid, volatile) function remove( address _token0, address _token1, uint256 _poolType ) external override(ICronV1PoolFactory) onlyOwner { address pool = poolMap[_token0][_token1][_poolType]; requireErrCode(pool != address(0), CronErrors.NON_EXISTING_POOL); poolMap[_token0][_token1][_poolType] = address(0); ICronV1PoolEnums.PoolType poolType = ICronV1PoolEnums.PoolType(_poolType); emit CronV1PoolRemoved(pool, _token0, _token1, poolType); } /// @notice Transfers ownership to `_newOwner`. Either directly or claimable by the new pending owner. /// Can only be invoked by the current `owner`. /// @param _newOwner Address of the new owner. /// @param _direct True if `_newOwner` should be set immediately. False if `_newOwner` needs to use `claimOwnership`. /// @param _renounce Allows the `_newOwner` to be `address(0)` if `_direct` and `_renounce` is True. Has no effect otherwise. function transferOwnership( address _newOwner, bool _direct, bool _renounce ) external override(ICronV1PoolFactory) onlyOwner { if (_direct) { requireErrCode(_newOwner != address(0) || _renounce, CronErrors.ZERO_TOKEN_ADDRESSES); emit OwnerChanged(owner, _newOwner); owner = _newOwner; pendingOwner = address(0); } else { pendingOwner = _newOwner; } } /// @notice Needs to be called by `pendingOwner` to claim ownership. function claimOwnership() external override(ICronV1PoolFactory) { address _pendingOwner = pendingOwner; requireErrCode(msg.sender == _pendingOwner, CronErrors.INVALID_PENDING_OWNER); emit OwnerChanged(owner, _pendingOwner); owner = _pendingOwner; pendingOwner = address(0); } /// @notice Gets existing pool for given address pair post sort and pool type /// @param _token0 address of token 0 /// @param _token1 address of token 1 /// @param _poolType type of pool function getPool( address _token0, address _token1, uint256 _poolType ) external view override(ICronV1PoolFactory) returns (address) { (address token0, address token1) = _token0 < _token1 ? (_token0, _token1) : (_token1, _token0); return poolMap[token0][token1][_poolType]; } }
// SPDX-License-Identifier: BUSL-1.1 // (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved // pragma solidity ^0.7.6; pragma experimental ABIEncoderV2; import { Math } from "./balancer-core-v2/lib/math/Math.sol"; import { IERC20 } from "./balancer-core-v2/lib/openzeppelin/IERC20.sol"; import { IERC20Decimals } from "./interfaces/IERC20Decimals.sol"; import { ReentrancyGuard } from "./balancer-core-v2/lib/openzeppelin/ReentrancyGuard.sol"; import { IMinimalSwapInfoPool } from "./balancer-core-v2/vault/interfaces/IMinimalSwapInfoPool.sol"; import { IBasePool } from "./balancer-core-v2/vault/interfaces/IBasePool.sol"; import { IVault } from "./balancer-core-v2/vault/interfaces/IVault.sol"; import { BalancerPoolToken } from "./balancer-core-v2/pools/BalancerPoolToken.sol"; import { IArbitrageurList } from "./interfaces/IArbitrageurList.sol"; import { ICronV1Pool } from "./interfaces/ICronV1Pool.sol"; import { ICronV1PoolFactory } from "./interfaces/ICronV1PoolFactory.sol"; import { ICronV1FactoryOwnerActions } from "./interfaces/pool/ICronV1FactoryOwnerActions.sol"; import { ICronV1PoolAdminActions } from "./interfaces/pool/ICronV1PoolAdminActions.sol"; import { ICronV1PoolArbitrageurActions } from "./interfaces/pool/ICronV1PoolArbitrageurActions.sol"; import { ICronV1PoolEnums } from "./interfaces/pool/ICronV1PoolEnums.sol"; import { ICronV1PoolEvents } from "./interfaces/pool/ICronV1PoolEvents.sol"; import { ICronV1PoolHelpers } from "./interfaces/pool/ICronV1PoolHelpers.sol"; import { C } from "./miscellany/Constants.sol"; import { requireErrCode, CronErrors } from "./miscellany/Errors.sol"; import { sqrt } from "./miscellany/Misc.sol"; import { BitPackingLib } from "./miscellany/BitPacking.sol"; import { VirtualOrders, OrderPools, Order, ExecVirtualOrdersMem, LoopMem, PriceOracle } from "./interfaces/Structs.sol"; /// @title An implementation of a Time-Weighted Average Market Maker on Balancer V2 Vault Pools. /// @author Zero Slippage (0slippage), Based upon the Paradigm paper TWAMM and the reference design /// created by frankieislost with optimizations from FRAX incorporated for gas efficiency. /// /// @notice For usage details, see the online Cron-Fi documentation at https://docs.cronfi.com/. /// /// @dev Uses Balancer math library for overflow/underflow checks on standard U256 containers. /// However, as many custom representations are used (i.e. non native word lengths) there /// are a number of explicit checks against the maximum of other word lengths. /// Furthermore there are unchecked operations (this code targets Solidity 0.7.x which /// didn't yet feature implicit arithmetic checks or have the 'unchecked' block feature) /// herein for reasons of efficiency or desired overflow. Wherever they appear they will /// be documented and accompanied with one of the following tags: /// - #unchecked /// - #overUnderFlowIntended /// Identified risks will be accompanied and described with the following tag: /// - #RISK /// /// @dev Conventions in the methods, variables and constants are as follows: /// /// Prefixes: /// /// - In constants, the prefix "Sn", where 1 <= n <= 4, denotes which slot the constant /// pertains too. There are four storage slots that are bitpacked. For example, /// "S2_OFFSET_ORACLE_TIMESTAMP" refers to the offset of the oracle timestamp in bit- /// packed storage slot 2. /// /// Suffixes: /// /// - The suffix of a variable name denotes the type contained within the variable. /// For instance "uint256 _incrementU96" is a 256-bit unsigned container representing /// the 96-bit value "_increment". /// In the case of "uint256 _balancerFeeDU1F18", the 256-bit unsigned container is /// representing a 19 digit decimal value with 18 fractional digits. In this scenario, /// the D=Decimal, U=Unsigned, F=Fractional. /// Finally, "uint128 valueU128F64" is a 128-bit container representing a 128-bit value /// with 64 fractional bits. /// /// - The suffix of a function name denotes what slot it is proprietary too as a /// matter of convention. While unchecked at run-time or by the compiler, the naming /// convention easily aids in understanding what slot a packed value is stored within. /// For instance the function "unpackFeeShiftS3" unpacks the fee shift from slot 3. /// If the value of slot 2 were passed to this method, the unpacked value would be /// incorrect. /// /// @dev Fee Points (FP) is a system used herein to calculate applicable fees. THESE ABSOLUTELY /// SHOULD NOT BE CONFUSED WITH BASIS POINTS--THEY ARE NOT BASIS POINTS! It consists of /// fees, such as a swap fee, expressed in FP. The swap fee is multiplied by the amount /// of token being swapped and divided by the total fee points (TOTAL_FP), which is 100,000, /// to obtain the fee. For instance, a swap fee of 0.050% can be realized as follows: /// /// token_in x FEE_BP /// swap_fee = ----------------- /// TOTAL_FP /// /// token_in x 50 /// = ----------------- /// 100000 /// contract CronV1Pool is ICronV1Pool, IMinimalSwapInfoPool, BalancerPoolToken, ReentrancyGuard { using Math for uint256; IVault private immutable VAULT; bytes32 public immutable override POOL_ID; IERC20 private immutable TOKEN0; IERC20 private immutable TOKEN1; PoolType public immutable override POOL_TYPE; uint16 private immutable ORDER_BLOCK_INTERVAL; uint24 private immutable MAX_ORDER_INTERVALS; uint256 private immutable ORDER_POOL0_PROCEEDS_SCALING; uint256 private immutable ORDER_POOL1_PROCEEDS_SCALING; address private immutable FACTORY; VirtualOrders private virtualOrders; /* Slot 1 Layout: * * The following variables are bit-mapped into uint256 slot1 in * container sizes related to their actual ranges as depicted below: * * 256-255 free (2-bits) * ------- * 254-245 shortTermFeeFP (10-bits) * ------- * 244-235 partnerFeeFP (10-bits) * ------- * 234-225 longTermFeeFP (10-bits) * ------- * 224-113 token0Orders (112-bits) * ------- * 112- 1 token1Orders (112-bits) * */ uint256 internal slot1; /* Slot 2 Layout: * * The following variables are bit-mapped into uint256 slot2 in * container sizes related to their actual ranges as depicted below: * * 256-225 oracleTimeStamp (32-bits) * ------- * 224-113 token0Proceeds (112-bits) * ------- * 112- 1 token1Proceeds (112-bits) * */ uint256 internal slot2; /* Slot 3 Layout: * * The following variables are bit-mapped into uint256 slot3 in * container sizes related to their actual ranges as depicted below: * * 256-226 free (31-bits) * ------- * 225-223 feeShiftU3 (3-bits) * ------- * 222-213 free (10-bits) * ------- * 212-193 free (20-bits) * ------- * 192- 97 token0CronFiFees (96-bits) * ------- * 96- 1 token1CronFiFees (96-bits) * */ uint256 internal slot3; /* Slot 4 Layout * * The following variables are bit-mapped into uint256 slot4 in * container sizes related to their actual ranges as depicted below: * * 256 paused (1-bit) * ------- * 255 cronFeeEnabled (1-bit) * ------- * 254 collectBalancerFees (1-bit) * ------- * 253 zeroCronFiFees (1-bit) * ------- * 252-193 balancerFeeDU1F18 (60-bits) * ------- * 192- 97 token0BalancerFees (96-bits) * ------- * 96- 1 token1BalancerFees (96-bits) * */ uint256 internal slot4; // @notice Uniswap V2 Style Oracle. // @dev Call function getOracleTimeStamp to get the timestamp associated // with the oracle price values in this state variable. PriceOracle internal priceOracle; mapping(address => bool) internal adminAddrMap; mapping(address => address) internal partnerContractAddrMap; address internal feeAddr; mapping(address => uint256[]) private orderIdMap; /// @notice Ensure that the modified function is called by an address that is the factory owner. /// @dev Cannot be used on Balancer Vault callbacks (onJoin, onExit, /// onSwap) because msg.sender is the Vault address. /// modifier senderIsFactoryOwner() { _senderIsFactoryOwner(); // #contractsize optimization _; } /// @notice Ensures that the modified function is called by an address with administrator privileges. /// @dev Cannot be used on Balancer Vault callbacks (onJoin, onExit, /// onSwap) because msg.sender is the Vault address. /// modifier senderIsAdmin() { _senderIsAdmin(); // #contractsize optimization _; } /// @notice Ensures the modified function is called by an address with arbitrage partner privileges. /// @dev Cannot be used on Balancer Vault callbacks (onJoin, onExit, /// onSwap) because msg.sender is the Vault address. /// modifier senderIsArbitragePartner() { _senderIsArbitragePartner(); // #contractsize optimization _; } /// @notice Ensures that the modified function is not executed if the pool is currently paused. /// modifier poolNotPaused() { _poolNotPaused(); // #contractsize optimization _; } /// @notice Creates an instance of the Cron-Fi TWAMM pool. A Cron-Fi TWAMM pool features virtual order management and /// virtualized reserves. Liquidity is managed through an instance of BalancerPoolToken. /// The fees associated with the pool are configurable at run-time. /// /// Importantly, the OBI cannot be changed after instantiation. If a pool's OBI is inappropriate to the /// properties of the pair of tokens, it is recommended to create a new pool. /// /// In the event of a failure, the pool can be paused which bypasses computation of virtual orders and allows /// liquidity to be removed and long-term virtual orders to be withdrawn and refunded. Other operations are /// blocked. /// /// Management of the pool is performed by administrators who are able set gross swap fee amounts and /// aribtrage partner status. /// /// The pool factory owner is able to set the status of administrators, enable Cron-Fi fees, modify the /// Cron-Fi fee address, adjust the fee-split between Cron-Fi and liquidity providers, and enable Balancer /// fees. /// /// Arbitrage partners are able to set and update a contract address that lists their arbitrageur's addresses, /// which are able to swap at reduced fees as an incentive to provide better long-term order execution by /// adjusting the bonding curve to compensate for the effect of virtual orders. These partners perform /// accounting and capture a percentage of the trades or capture fees in another way which are periodically /// remitted to the pool, rewarding the liquidity providers. This may be thought of as a constructive pay for /// order flow or Maximal Extractable Value (MEV) recapture. /// /// @param _token0Inst The contract instance for token 0. /// @param _token1Inst The contract instance for token 1. /// @param _vaultInst The Balancer Vault instance this pool be a member of. /// @param _poolName The name for this pool. /// @param _poolSymbol The symbol for this pool. /// @param _poolType A value in the enumeration PoolType that controls the initial fee values and Order Block /// Interval (OBI) of the pool. See the documentation for the PoolType enumeration for details. /// constructor( IERC20 _token0Inst, IERC20 _token1Inst, IVault _vaultInst, string memory _poolName, string memory _poolSymbol, PoolType _poolType ) BalancerPoolToken(_poolName, _poolSymbol) { // Only factory can create pools, or else _senderIsFactoryOwner will revert: FACTORY = msg.sender; bytes32 poolIdValue = _vaultInst.registerPool(IVault.PoolSpecialization.TWO_TOKEN); IERC20[] memory tokens = new IERC20[](2); tokens[C.INDEX_TOKEN0] = _token0Inst; tokens[C.INDEX_TOKEN1] = _token1Inst; _vaultInst.registerTokens( poolIdValue, tokens, new address[](2) /* assetManagers */ ); VAULT = _vaultInst; POOL_ID = poolIdValue; TOKEN0 = _token0Inst; TOKEN1 = _token1Inst; POOL_TYPE = _poolType; // Compute the scaling factors for the order pool proceeds of each token. The addition of 1 is buffering the scaled // result to preserve additional precision. // uint256 token0Decimals = IERC20Decimals(address(_token0Inst)).decimals(); requireErrCode( C.MIN_DECIMALS <= token0Decimals && token0Decimals <= C.MAX_DECIMALS, CronErrors.UNSUPPORTED_TOKEN_DECIMALS ); ORDER_POOL0_PROCEEDS_SCALING = (10**(token0Decimals + 1)); uint256 token1Decimals = IERC20Decimals(address(_token1Inst)).decimals(); requireErrCode( C.MIN_DECIMALS <= token1Decimals && token1Decimals <= C.MAX_DECIMALS, CronErrors.UNSUPPORTED_TOKEN_DECIMALS ); ORDER_POOL1_PROCEEDS_SCALING = (10**(token1Decimals + 1)); // NOTE: Conditional assignment style / ternary operator hell required for immutables. // ORDER_BLOCK_INTERVAL = (_poolType == PoolType.Stable) ? C.STABLE_OBI : (_poolType == PoolType.Liquid) ? C.LIQUID_OBI : C.VOLATILE_OBI; MAX_ORDER_INTERVALS = (_poolType == PoolType.Stable) ? C.STABLE_MAX_INTERVALS : (_poolType == PoolType.Liquid) ? C.LIQUID_MAX_INTERVALS : C.VOLATILE_MAX_INTERVALS; if (_poolType == PoolType.Stable) { uint256 localSlot1 = BitPackingLib.packU10(0, C.STABLE_ST_FEE_FP, C.S1_OFFSET_SHORT_TERM_FEE_FP); localSlot1 = BitPackingLib.packU10(localSlot1, C.STABLE_ST_PARTNER_FEE_FP, C.S1_OFFSET_PARTNER_FEE_FP); slot1 = BitPackingLib.packU10(localSlot1, C.STABLE_LT_FEE_FP, C.S1_OFFSET_LONG_TERM_FEE_FP); } else if (_poolType == PoolType.Liquid) { uint256 localSlot1 = BitPackingLib.packU10(0, C.LIQUID_ST_FEE_FP, C.S1_OFFSET_SHORT_TERM_FEE_FP); localSlot1 = BitPackingLib.packU10(localSlot1, C.LIQUID_ST_PARTNER_FEE_FP, C.S1_OFFSET_PARTNER_FEE_FP); slot1 = BitPackingLib.packU10(localSlot1, C.LIQUID_LT_FEE_FP, C.S1_OFFSET_LONG_TERM_FEE_FP); } else { // PoolType.Volatile uint256 localSlot1 = BitPackingLib.packU10(0, C.VOLATILE_ST_FEE_FP, C.S1_OFFSET_SHORT_TERM_FEE_FP); localSlot1 = BitPackingLib.packU10(localSlot1, C.VOLATILE_ST_PARTNER_FEE_FP, C.S1_OFFSET_PARTNER_FEE_FP); slot1 = BitPackingLib.packU10(localSlot1, C.VOLATILE_LT_FEE_FP, C.S1_OFFSET_LONG_TERM_FEE_FP); } slot3 = BitPackingLib.packFeeShiftS3(slot3, C.DEFAULT_FEE_SHIFT); uint256 localSlot4 = BitPackingLib.packBit(0, 0, C.S4_OFFSET_PAUSE); localSlot4 = BitPackingLib.packBit(localSlot4, 0, C.S4_OFFSET_CRON_FEE_ENABLED); localSlot4 = BitPackingLib.packBit(localSlot4, 1, C.S4_OFFSET_COLLECT_BALANCER_FEES); slot4 = BitPackingLib.packBit(localSlot4, 1, C.S4_OFFSET_ZERO_CRONFI_FEES); adminAddrMap[C.CRON_DEPLOYER_ADMIN] = true; emit AdministratorStatusChange(msg.sender, C.CRON_DEPLOYER_ADMIN, true); emit FeeAddressChange(msg.sender, C.NULL_ADDR); } /// @notice Called by the vault when a user calls IVault.swap. Can be used to perform a Short-Term (ST) /// swap, Long-Term (LT) swap, or Partner swap /// ST swaps and Partner swaps behave like traditional Automated Market Maker atomic swaps /// (think Uniswap V2 swaps). /// LT swaps are virtual orders and behave differently, executing over successive blocks until /// their expiry. Each LT swap is assigned an order id that is logged in a LongTermSwap event and /// can also be fetched using getOrderIds for a given address. LT swaps can be withdrawn or /// cancelled through the IVault.exit function (see onExitPool documentation). /// @param _swapRequest Is documented in Balancer's IPoolSwapStructs.sol. However, the userData field /// of this _swapRequest struct is a uint256 value, swapTypeU, followed by another /// uint256 value, argument, detailed below: /// * swapTypeU is decoded into the enum SwapType and determines if the transaction /// is a RegularSwap, LongTermSwap, or PartnerSwap. /// Min. = 0, Max. = 3 /// * argument is a value, the use of which depends upon the SwapType value passed /// into swapTypeU: /// - swapTypeU=0 (RegularSwap): argument is ignored / not used. /// - swapTypeU=1 (LongTermSwap): argument is the number of order intervals for /// the LT trade before expiry. /// - swapTypeU=2 (PartnerSwap): argument is the Partner address stored in a /// uint256. It is used to loop up the Partner's /// current arbitrage list contract address. /// Delegates: /// If the specified swapType is a LongTermSwap, the _swapRequest.to field can be /// used to specify a LT-Swap delegate. The delegate account is able to withdraw or /// cancel the LT-swap on behalf of the order owner (_swapRequest.from) at any time, /// so long as the recipient account specified for proceeds or refunds is the order /// owner. (The order owner does not have this restriction and direct proceeds or /// refunds to any desired account.) /// If the specified _swapRequest.to field is the null address or the order owner, /// then the delegate is disabled (and set to the null address). /// @param _currentBalanceTokenInU112 The Balancer Vault balance of the token being sold to the pool. /// Min. = 0, Max. = (2**112) - 1 /// @param _currentBalanceTokenOutU112 The Balancer Vault balance of the token being bought from the pool. /// Min. = 0, Max. = (2**112) - 1 /// @return amountOutU112 The amount of token being bought from the pool in this swap. For LT swaps this /// will always be zero. Proceeds from an LT swap can be withdrawn or the order /// refunded with an appropriate call to the IVault.exit function (see onExitPool /// documentation). /// function onSwap( SwapRequest memory _swapRequest, uint256 _currentBalanceTokenInU112, uint256 _currentBalanceTokenOutU112 ) external override(IMinimalSwapInfoPool) poolNotPaused returns (uint256 amountOutU112) { requireErrCode(msg.sender == address(VAULT), CronErrors.NON_VAULT_CALLER); // NOTE: Not checking for balance overflow (amount + _currentBalanceTokenInU112) because this is // handled by Balancer error BAL#526 BALANCE_TOTAL_OVERFLOW // #savegas #savesize IERC20 tokenIn = _swapRequest.tokenIn; address from = _swapRequest.from; uint256 amount = _swapRequest.amount; requireErrCode(_swapRequest.kind == IVault.SwapKind.GIVEN_IN, CronErrors.UNSUPPORTED_SWAP_KIND); // This style of decoding the data into the enum saves 16b of contract size. (uint256 swapTypeU, uint256 argument) = abi.decode(_swapRequest.userData, (uint256, uint256)); SwapType swapType = SwapType(swapTypeU); bool token0To1 = (tokenIn == TOKEN0); (uint256 token0ReserveU112, uint256 token1ReserveU112) = _executeVirtualOrders( token0To1 ? _currentBalanceTokenInU112 : _currentBalanceTokenOutU112, token0To1 ? _currentBalanceTokenOutU112 : _currentBalanceTokenInU112, block.number ); if (swapType == SwapType.LongTermSwap) { address delegate = _swapRequest.to; uint256 orderId = _longTermSwap(from, delegate, token0To1, amount, argument); emit LongTermSwap(from, delegate, address(tokenIn), amount, argument, orderId); } else { if (swapType == SwapType.PartnerSwap) { address contractAddress = partnerContractAddrMap[address(argument)]; requireErrCode(IArbitrageurList(contractAddress).isArbitrageur(from), CronErrors.SENDER_NOT_PARTNER); } amountOutU112 = _shortTermSwap( token0To1, (swapType == SwapType.RegularSwap), amount, token0ReserveU112, token1ReserveU112 ); emit ShortTermSwap(from, address(tokenIn), amount, amountOutU112, swapTypeU); } } /// @notice Called by the Vault when a user calls IVault.joinPool. Can be use to add liquidity to /// the pool in exchange for Liquidity Provider (LP) pool tokens or to reward the pool with /// liquidity (MEV rewards from arbitrageurs). /// WARNING: The initial liquidity provider, in a call to join the pool with joinTypeU=0 /// (JoinType.Join), will sacrifice MINIMUM_LIQUIDITY, 1000, Liquidity Provider (LP) /// tokens. This may be an insignificant sacrifice for tokens with fewer decimal /// places and high worth (i.e. WBTC). /// Importantly, the reward capability remains when the pool is paused to mitigate any /// possible issue with underflowed pool reserves computed by differencing the pool accounting /// from the pool token balances. /// @param _poolId The ID for this pool in the Balancer Vault /// @param _sender is the account performing the Join or Reward transaction (typically an LP or MEV reward /// contract, respectively). /// @param _recipient is the account designated to receive pool shares in the form of LP tokens when /// Joining the pool. Can be set to _sender if sender wishes to receive the tokens /// and Join Events. /// @param _currentBalancesU112 an array containing the Balancer Vault balances of Token 0 and Token 1 /// in this pool. The balances are in the same order that IVault.getPoolTokens /// returns. /// Min. = 0, Max. = (2**112) - 1 /// @param _protocolFeeDU1F18 the Balancer protocol fee. /// Min. = 0, Max. = 10**18 /// @param _userData is uint256 value, joinTypeU, followed by an array of 2 uint256 values, amounts, /// and another array of 2 uint256 values, minAmounts, detailed below: /// * joinTypeU is decoded into the enum JoinType and determines if the transaction is /// a Join or Reward. /// Min. = 0, Max. = 1 /// * amountsInU112 are the amount of Token 0 and Token 1 to Join or Reward the pool /// with, passed in the same array ordering that IVault.getPoolTokens /// returns. /// Min. = 0, Max. = (2**112) - 1 /// * minAmountsU112 are the minimum amount of Token 0 and Token 1 prices at which /// to Join the pool (protecting against sandwich attacks), passed /// in the same array ordering that IVault.getPoolTokens returns. /// The minAmountsU112 values are ignored unless joinTypeU is /// 0 (JoinType.Join). In the initial join, these values are /// ignored. /// Min. = 0, Max. = (2**112) - 1 /// @return amountsInU112 is the amount of Token 0 and Token 1 provided to the pool as part of a Join or /// Reward transaction. Values are returned in the same array ordering that /// IVault.getPoolTokens returns. /// Min. = 0, Max. = (2**112) - 1 /// @return dueProtocolFeeAmountsU96 the amount of Token 0 and Token 1 collected by the pool for /// Balancer. Values are returned in the same array ordering that /// IVault.getPoolTokens returns. /// Min. = 0, Max. = (2**96) - 1 /// function onJoinPool( bytes32 _poolId, address _sender, address _recipient, uint256[] memory _currentBalancesU112, uint256, /* lastChangeBlock - not used */ uint256 _protocolFeeDU1F18, bytes calldata _userData ) external override(IBasePool) returns (uint256[] memory amountsInU112, uint256[] memory dueProtocolFeeAmountsU96) { _poolSafetyChecks(msg.sender, _poolId); JoinType joinType; uint256[] memory minAmountsU112; // Following block is a Stack Too Deep Workaround { // This style of decoding the data into the enum saves 16b of contract size. uint256 joinTypeU; (joinTypeU, amountsInU112, minAmountsU112) = abi.decode(_userData, (uint256, uint256[], uint256[])); joinType = JoinType(joinTypeU); } // NOTE: Not checking for balance overflows (amountsInU112 + _currentBalancesU112) because this is // handled by Balancer error BAL#526 BALANCE_TOTAL_OVERFLOW //////////////////////////////////////////////////////////////////////////////// // // // BEGIN WARNING: Moving the following 3 lines of code or separating them // // results in increased contract size (3b - ~53b or more // // depending on where the code is moved or how separated). // // // //////////////////////////////////////////////////////////////////////////////// // #savesize #savegas uint256 token0InU112 = amountsInU112[C.INDEX_TOKEN0]; uint256 token1InU112 = amountsInU112[C.INDEX_TOKEN1]; uint256 amountLP; //////////////////////////////////////////////////////////////////////////////// // END WARNING // //////////////////////////////////////////////////////////////////////////////// // NOTE: If the pool is paused, JoinType.Join reverts. JoinType.Reward does not revert, but proceeds // without running execute virtual orders deliberately; this is because it is a fallback mitigation // if _calculateReserves within the execute virtual orders process fails due to unforeseen finite // precision effects (specifically if Balancer's accounting is less than the sum of this pool's // orders, proceeds, and fees for either pool token--in this case a reward can be used to increase // the balancer vault accounting preventing the failure and allowing withdraws, cancels, and pool // exits that would otherwise be blocked by the failing _calculateReserves call). // // If the pool is not paused, execute virtual orders is run for JoinType.Reward to ensure the added // liquidity applies to the newest trades (i.e. those after the JoinType.Reward transaction). // if (!isPaused()) { uint256 token0ReserveU112; uint256 token1ReserveU112; if ((joinType == JoinType.Join && totalSupply() != 0) || (joinType == JoinType.Reward)) { (token0ReserveU112, token1ReserveU112) = _executeVirtualOrders( _currentBalancesU112[C.INDEX_TOKEN0], _currentBalancesU112[C.INDEX_TOKEN1], block.number ); } if (joinType == JoinType.Join) { _joinMinimumCheck( token0InU112, token1InU112, minAmountsU112[C.INDEX_TOKEN0], minAmountsU112[C.INDEX_TOKEN1], token0ReserveU112, token1ReserveU112 ); amountLP = _join(_recipient, token0InU112, token1InU112, token0ReserveU112, token1ReserveU112); } } else { requireErrCode(joinType != JoinType.Join, CronErrors.POOL_PAUSED); } // When amountLP = 0, the PoolJoin event log doubles as a Reward event log. emit PoolJoin(_sender, _recipient, token0InU112, token1InU112, amountLP); dueProtocolFeeAmountsU96 = _handleBalancerFees(_protocolFeeDU1F18); } /// @notice Called by the Vault when a user calls IVault.exitPool. Can be used to remove liquidity from /// the pool in exchange for Liquidity Provider (LP) pool tokens, to withdraw proceeds of a /// Long-Term (LT) order, to cancel an LT order, or by the factory owner to withdraw protocol /// fees if they are being collected. /// @param _poolId is the ID for this pool in the Balancer Vault /// @param _sender is the account performing the liquidity removal, LT order withdrawl, LT order /// cancellation or the factory owner withdrawing fees. /// For long term orders, a "delegate" may be specified, this address is able to /// perform LT order withdraws and cancellations on behalf of the LT swap owner as /// long as the recipient is the LT swap owner. /// @param _recipient For LT swaps the recipient must always be the original order owner (the address /// that issued the order) if a "delegate" address is performing the withdrawl or /// cancellation. If the order owner is performing the withdrawl or cancellation, the /// recipient can be set to whatever destination address desired. /// For other exit types (Exit & FeeWithdraw), the recipient can be set as desired /// so long as the sender is set to the authorized address. /// @param _currentBalancesU112 is an array containing the Balancer Vault balances of Token 0 and Token 1 /// in this pool. The balances are in the same order that IVault.getPoolTokens /// returns. /// Min. = 0, Max. = (2**112) - 1 /// @param _protocolFeeDU1F18 is the Balancer protocol fee. /// Min. = 0, Max. = 10**18 /// @param _userData is uint256 value, exitTypeU, followed by a uint256, argument, detailed below: /// * exitTypeU is decoded into the enum ExitType and determines if the transaction is /// an Exit, Withdraw, Cancel, or FeeWithdraw. /// Min. = 0, Max. = 3 /// * argument is used differently based on the ExitType value passed into exitTypeU: /// - exitTypeU=0 (Exit): argument is the number of LP tokens to /// redeem on Exit. /// - exitTypeU=1 (Withdraw): argument is the LT Swap order ID. /// - exitTypeU=2 (Cancel): argument is the LT Swap order ID. /// - exitTypeU=3 (FeeWithdraw): argument is ignored / not used. /// @return amountsOutU112 is the amount of Token 0 and Token 1 provided by the pool as part of an Exit, /// LT Swap Withdrawl, LT Swap Cancel, or Fee Withdraw transaction. Values are /// returned in the same array ordering that IVault.getPoolTokens returns. /// Min. = 0, Max. = (2**112) - 1 /// @return dueProtocolFeeAmountsU96 the amount of Token 0 and Token 1 collected by the pool for /// Balancer. Values are returned in the same array ordering that /// IVault.getPoolTokens returns. /// Min. = 0, Max. = (2**96) - 1 function onExitPool( bytes32 _poolId, address _sender, address _recipient, uint256[] memory _currentBalancesU112, uint256, /* lastChangeBlock - not used */ uint256 _protocolFeeDU1F18, bytes calldata _userData ) external override(IBasePool) returns (uint256[] memory amountsOutU112, uint256[] memory dueProtocolFeeAmountsU96) { _poolSafetyChecks(msg.sender, _poolId); ExitType exitType; uint256 argument; // Following block is a Stack Too Deep Workaround { // This style of decoding the data into the enum saves 16b of contract size. uint256 exitTypeU; (exitTypeU, argument) = abi.decode(_userData, (uint256, uint256)); exitType = ExitType(exitTypeU); } uint256 token0OutU112; uint256 token1OutU112; if (exitType == ExitType.FeeWithdraw) { (token0OutU112, token1OutU112) = _withdrawCronFees(_sender); } else { // Following block is a Stack Too Deep Workaround { // _executeVirtualOrders must be run for all exit types below, but it is in // this block b/c token0ReserveU112 and token1ReserveU112 variables are only for the // ExitType.Exit and cause Stack Too Deep otherwise. (uint256 token0ReserveU112, uint256 token1ReserveU112) = _executeVirtualOrders( _currentBalancesU112[C.INDEX_TOKEN0], _currentBalancesU112[C.INDEX_TOKEN1], block.number ); if (exitType == ExitType.Exit) { (token0OutU112, token1OutU112) = _exit(_sender, argument, token0ReserveU112, token1ReserveU112); } } if (exitType == ExitType.Withdraw || exitType == ExitType.Cancel) { // NOTE: For all calls in this else block: // - argument is the Order ID // - _sender must be the original virtual order _sender (token0OutU112, token1OutU112) = _withdrawLongTermSwapWrapper( argument, _sender, _recipient, exitType == ExitType.Cancel ); } } amountsOutU112 = new uint256[](2); amountsOutU112[C.INDEX_TOKEN0] = token0OutU112; amountsOutU112[C.INDEX_TOKEN1] = token1OutU112; dueProtocolFeeAmountsU96 = _handleBalancerFees(_protocolFeeDU1F18); } /// @notice Set the administrator status of the provided address, _admin. Status "true" gives /// administrative privileges, "false" removes privileges. /// @param _admin The address to add or remove administrative privileges from. /// @param _status Whether to grant (true) or deny (false) administrative privileges. /// @dev CAREFUL! You can remove all administrative privileges, rendering the contract unmanageable. /// @dev NOTE: Must be called by the factory owner. /// function setAdminStatus(address _admin, bool _status) external override(ICronV1FactoryOwnerActions) senderIsFactoryOwner nonReentrant { adminAddrMap[_admin] = _status; emit AdministratorStatusChange(msg.sender, _admin, _status); } /// @notice Enables Cron-Fi fee collection for Long-Term swaps when the provided address, /// _feeDestination is not the null address. /// @param _feeDestination The address that can collect Cron-Fi Swap fees. If set to the null address, /// no Cron-Fi Long-Term swap fees are collected. /// @dev CAREFUL! Only the _feeDestination address can collect Cron-Fi fees from the pool. /// @dev NOTE: Must be called by the factory owner. /// function setFeeAddress(address _feeDestination) external override(ICronV1FactoryOwnerActions) senderIsFactoryOwner nonReentrant { feeAddr = _feeDestination; uint256 cronFeeEnabled = (_feeDestination != C.NULL_ADDR) ? 1 : 0; slot4 = BitPackingLib.packBit(slot4, cronFeeEnabled, C.S4_OFFSET_CRON_FEE_ENABLED); emit FeeAddressChange(msg.sender, _feeDestination); } /// @notice Sets whether the pool is paused or not. When the pool is paused: /// * New swaps of any kind cannot be issued. /// * Liquidity cannot be provided. /// * Virtual orders are not executed for the remainder of allowable /// operations, which include: removing liquidity, cancelling or /// withdrawing a Long-Term swap order, /// This is a safety measure that is not a part of expected pool operations. /// @param _pauseValue Pause the pool (true) or not (false). /// @dev NOTE: Must be called by an administrator. /// function setPause(bool _pauseValue) external override(ICronV1PoolAdminActions) senderIsAdmin nonReentrant { slot4 = BitPackingLib.packBit(slot4, _pauseValue ? 1 : 0, C.S4_OFFSET_PAUSE); emit BoolParameterChange(msg.sender, BoolParamType.Paused, _pauseValue); } /// @notice Set fee parameters. /// @param _paramTypeU A numerical value corresponding to the enum ParamType (see documentation /// for that above or values and corresponding ranges below). /// @param _value A value to set the specified parameter given in _paramTypeU. The values and /// their ranges are as follows: /// /// Short-Term Swap Fee Points: /// * _paramTypeU = 0 (ParamType.SwapFeeFP) /// * 0 <= _value <= C.MAX_FEE_FP (1000, ~1.000%) /// /// Partner Swap Fee Points: /// * _paramTypeU = 1 (ParamType.PartnerFeeFP) /// * 0 <= _value <= C.MAX_FEE_FP (1000, ~1.000%) /// /// Long-Term Swap Fee Points: /// * _paramTypeU = 2 (ParamType.LongSwapFeeFP) /// * 0 <= _value <= C.MAX_FEE_FP (1000, ~1.000%) /// /// @dev NOTE: Total FP = 100,000. Thus a fee portion is the number of FP out of 100,000. /// @dev NOTE: Must be called by an administrator. /// function setParameter(uint256 _paramTypeU, uint256 _value) external override(ICronV1PoolAdminActions) senderIsAdmin nonReentrant { ParamType paramType = ParamType(_paramTypeU); // 1. Determine the bit-offset for any values that require an offset to be set: // uint256 offset; if (paramType == ParamType.SwapFeeFP) { offset = C.S1_OFFSET_SHORT_TERM_FEE_FP; } else if (paramType == ParamType.PartnerFeeFP) { offset = C.S1_OFFSET_PARTNER_FEE_FP; } else if (paramType == ParamType.LongSwapFeeFP) { offset = C.S1_OFFSET_LONG_TERM_FEE_FP; } // else offset can be zero. // 2. Map _paramType numerically to the corresponding slot and then do numerical limits test // and set the value if conformant. Otherwise revert with a numerical limit error from a // call to BitPackingLib or a parameter error. // uint256 value = _value; if (paramType <= ParamType.LongSwapFeeFP && offset != 0 && _value <= C.MAX_FEE_FP) { slot1 = BitPackingLib.packU10(slot1, value, offset); } else { requireErrCode( false, /* error intentionally, 9b smaller than dedicated revert refactor of this */ CronErrors.PARAM_ERROR ); } emit ParameterChange(msg.sender, paramType, value); } /// @notice Enable or disable the collection of Balancer Fees. When enabled Balancer takes a /// a portion of every fee collected in the pool. The pool remits fees to Balancer /// automatically when onJoinPool and onExitPool are called. Disabling balancer fees /// through this function supersedes any setting of balancer fee values in onJoinPool /// and onExitPool. /// @param _collectBalancerFee When true, Balancer fees are collected, when false they are /// not collected. /// @dev NOTE: Must be called by the factory owner. /// function setCollectBalancerFees(bool _collectBalancerFee) external override(ICronV1FactoryOwnerActions) senderIsFactoryOwner nonReentrant { slot4 = BitPackingLib.packBit(slot4, _collectBalancerFee ? 1 : 0, C.S4_OFFSET_COLLECT_BALANCER_FEES); emit BoolParameterChange(msg.sender, BoolParamType.CollectBalancerFees, _collectBalancerFee); } /// @notice Sets the fee shift that splits Long-Term (LT) swap fees remaining after Balancer's cut /// between the Liquidity Providers (LP) and Cron-Fi, if Cron-Fi fee collection is enabled. /// @param _feeShift A value between 1 and 4. Specifiying invalid values results in no-operation /// (percentages are approximate). The implication of these values are outlined /// below and are only applicable if Cron-Fi fees are being collected: /// /// _feeShift = 1: /// LP gets 2 fee shares (~66%), Cron-Fi gets 1 fee share (~33%) /// /// _feeShift = 2: /// LP gets 4 fee shares (~80%), Cron-Fi gets 1 fee share (~20%) /// /// _feeShift = 3: /// LP gets 8 fee shares (~88%), Cron-Fi gets 1 fee share (~12%) /// /// _feeShift = 4: /// LP gets 16 fee shares (~94%), Cron-Fi gets 1 fee share (~6%) /// /// @dev NOTE: Must be called by the factory owner. /// function setFeeShift(uint256 _feeShift) external override(ICronV1FactoryOwnerActions) senderIsFactoryOwner nonReentrant { requireErrCode(_feeShift >= 1 && _feeShift <= 4, CronErrors.PARAM_ERROR); slot3 = BitPackingLib.packFeeShiftS3(slot3, _feeShift); emit FeeShiftChange(msg.sender, _feeShift); } /// @notice Sets the arbitrageur list contract address, _arbitrageList, for an arbitrage /// partner, _arbPartner. To clear an arbitrage partner, set _arbitrageList to the /// null address. /// @param _arbPartner The address of the arbitrage partner. This should be the aribtrage /// partner's public address and shareable with members of the the /// arbitrage list contract. /// @param _arbitrageList The address of a deployed arbitrageur list contract for the /// specified arbitrage partner. The deployed contract should /// conform to the interface IArbitrageurList. /// /// @dev NOTE: Must be called by an administrator. /// function setArbitragePartner(address _arbPartner, address _arbitrageList) external override(ICronV1PoolAdminActions) senderIsAdmin nonReentrant { partnerContractAddrMap[_arbPartner] = _arbitrageList; emit UpdatedArbitragePartner(msg.sender, _arbPartner, _arbitrageList); } /// @notice Advances the specified arbitrage partner (msg.sender) arbitrageur list /// contract to the newest contract, if available. See IArbitrageurList for /// details on the calls made by this contract to that interface's nextList /// function. /// @return the new arbitrageur list contract address. /// @dev NOTE: Must be called by an arbitrage partner. /// function updateArbitrageList() external override(ICronV1PoolArbitrageurActions) senderIsArbitragePartner nonReentrant returns (address) { address currentList; address oldList = partnerContractAddrMap[msg.sender]; address nextList = oldList; do { currentList = nextList; nextList = IArbitrageurList(currentList).nextList(); } while (nextList != C.NULL_ADDR); partnerContractAddrMap[msg.sender] = currentList; emit UpdatedArbitrageList(msg.sender, oldList, currentList); return currentList; } /// @notice Executes active virtual orders, Long-Term swaps, since the last virtual order block /// executed, updating reserve and other state variables to the current block. /// @param _maxBlock A block to update the virtual orders to. In most situations this would be /// the current block, however if the pool has been inactive for a considerable /// duration, specifying an earlier block allows gas use to be reduced in the event /// that the gas needed to update to the current block for a transaction to be /// performed exceeds the nominal or extended amounts available to an Ethereum /// transaction (15M & 30M respectively at the time of this writing). /// If the specified max block preceeds the last virtual order block then the /// current block number is automatically used. /// function executeVirtualOrdersToBlock(uint256 _maxBlock) external override(ICronV1PoolArbitrageurActions) nonReentrant { _triggerVaultReentrancyCheck(); (, uint256[] memory balances, ) = VAULT.getPoolTokens(POOL_ID); _executeVirtualOrders(balances[C.INDEX_TOKEN0], balances[C.INDEX_TOKEN1], _maxBlock); emit ExecuteVirtualOrdersEvent(msg.sender, _maxBlock); } /// @notice Get the virtual price oracle data for the pool at the specified block, _maxBlock. /// /// IMPORTANT - This function calls _getVirtualReserves, which triggers a re-entrancy check. Due /// to contract size challanges, there is no explicit call to that re-entrancy check /// in this function, where it's presence would be more obvious. /// /// IMPORTANT - This function does not meaningfully modify state despite the lack of a "view" /// designator for state mutability. (The call to _triggerVaultReentrancyCheck /// Unfortunately prevents the "view" designator as meaningless value is written /// to state to trigger a reentracy check). /// /// Runs virtual orders from the last virtual order block up to the current block to provide /// visibility into the current accounting for the pool. /// If the pool is paused, this function reflects the accounting values of the pool at /// the last virtual order block (i.e. it does not execute virtual orders to deliver the result). /// @param _maxBlock is the block to determine the virtual oracle values at. Its value must be /// greater than the last virtual order block and less than or equal to the /// current block. Otherwise, current block is used in the computation and /// reflected in the return value, blockNumber. /// @return timestamp is the virtual timestamp in seconds corresponding to the price oracle /// values. /// @return token0U256F112 The virtual cumulative price of Token0 measured in amount of /// Token1 * seconds at timestamp. /// @return token1U256F112 The virtual cumulative price of Token1 measured in amount of /// Token0 * seconds at timestamp. /// @return blockNumber The block that the virtual oracle values were computed at. Should /// match parameter _maxBlock, unless _maxBlock was not greater than the /// last virtual order block or less than or equal to the current block. /// @dev Check that blockNumber matches _maxBlock to ensure that _maxBlock was correctly /// specified. /// function getVirtualPriceOracle(uint256 _maxBlock) external override(ICronV1PoolHelpers) returns ( uint256 timestamp, uint256 token0U256F112, uint256 token1U256F112, uint256 blockNumber ) { ExecVirtualOrdersMem memory evoMem; (evoMem, blockNumber) = _getVirtualReserves( _maxBlock, false /* not paused */ ); if (virtualOrders.lastVirtualOrderBlock > 0) { uint32 timeElapsed; (timeElapsed, timestamp) = _getOracleTimeData(blockNumber); token0U256F112 = priceOracle.token0U256F112; token1U256F112 = priceOracle.token1U256F112; if (timeElapsed > 0) { // #overUnderFlowIntended // The UQ256x112 incremented result relies upon and expects overflow // and is unchecked. The reason is that it is the difference between // price oracle samples over time that matters, not the absolute value. // As noted above, see the Uniswap V2 whitepaper. token0U256F112 += evoMem.token0OracleU256F112; token1U256F112 += evoMem.token1OracleU256F112; } } } /// @notice Returns the TWAMM pool's reserves after the non-stateful execution of all virtual orders /// up to the specified maximum block (unless an invalid block is specified, which results /// in execution to the current block). /// /// IMPORTANT - This function calls _getVirtualReserves, which triggers a re-entrancy check. Due /// to contract size challanges, there is no explicit call to that re-entrancy check /// in this function, where it's presence would be more obvious. /// /// IMPORTANT - This function does not meaningfully modify state despite the lack of a "view" /// designator for state mutability. (The call to _triggerVaultReentrancyCheck /// Unfortunately prevents the "view" designator as meaningless value is written /// to state to trigger a reentracy check). /// /// @param _maxBlock a block to update virtual orders to. If less than or equal to the last virtual order /// block or greater than the current block, the value is set to the current /// block number. /// @param _paused is true to indicate the result should be returned as though the pool is in a paused /// state where virtual orders are not executed and only withdraw, cancel and liquidations /// are possible (check function isPaused to see if the pool is in that state). If false /// then the virtual reserves are computed from virtual order execution to the specified /// block. /// @return blockNumber The block that the virtual reserve values were computed at. Should /// match parameter _maxBlock, unless _maxBlock was not greater than the /// last virtual order block or less than or equal to the current block. /// @return token0ReserveU112 virtual reserves of Token0 in the TWAMM pool at blockNumber. /// @return token1ReserveU112 virtual reserves of Token1 in the TWAMM pool at blockNumber. /// @return token0OrdersU112 virtual amount of Token0 remaining to be sold to the pool in LT swap orders /// at blockNumber. /// @return token1OrdersU112 virtual amount of Token1 remaining to be sold to the pool in LT swap orders /// at blockNumber. /// @return token0ProceedsU112 virtual amount of Token0 purchased from the pool by LT swap orders /// at blockNumber. /// @return token1ProceedsU112 virtual amount of Token1 purchased from the pool by LT swap orders /// at blockNumber. /// @return token0BalancerFeesU96 virtual Balancer fees collected for all types of Token0-->Token1 swaps /// at blockNumber. /// @return token1BalancerFeesU96 virtual Balancer fees collected for all types of Token1-->Token0 swaps /// at blockNumber. /// @return token0CronFiFeesU96 virtual Cron-Fi fees collected for Token0-->Token1 Long-Term swaps at /// blockNumber. /// @return token1CronFiFeesU96 virtual Cron-Fi fees collected for Token1-->Token0 Long-Term swaps at /// blockNumber. /// function getVirtualReserves(uint256 _maxBlock, bool _paused) external override(ICronV1PoolHelpers) returns ( uint256 blockNumber, uint256 token0ReserveU112, uint256 token1ReserveU112, uint256 token0OrdersU112, uint256 token1OrdersU112, uint256 token0ProceedsU112, uint256 token1ProceedsU112, uint256 token0BalancerFeesU96, uint256 token1BalancerFeesU96, uint256 token0CronFiFeesU96, uint256 token1CronFiFeesU96 ) { ExecVirtualOrdersMem memory evoMem; (evoMem, blockNumber) = _getVirtualReserves(_maxBlock, _paused); token0ReserveU112 = evoMem.token0ReserveU112; token1ReserveU112 = evoMem.token1ReserveU112; // Note that the order difference is a subtraction (virtual orders change orders // by selling them into the pool reserves): (uint256 t0Orders, uint256 t1Orders) = BitPackingLib.unpackPairU112(slot1); token0OrdersU112 = t0Orders - evoMem.token0OrdersU112; token1OrdersU112 = t1Orders - evoMem.token1OrdersU112; (uint256 t0Proceeds, uint256 t1Proceeds) = BitPackingLib.unpackPairU112(slot2); token0ProceedsU112 = t0Proceeds + evoMem.token0ProceedsU112; token1ProceedsU112 = t1Proceeds + evoMem.token1ProceedsU112; (uint256 t0BalFees, uint256 t1BalFees) = BitPackingLib.unpackPairU96(slot4); token0BalancerFeesU96 = t0BalFees + evoMem.token0BalancerFeesU96; token1BalancerFeesU96 = t1BalFees + evoMem.token1BalancerFeesU96; (uint256 t0CronFiFees, uint256 t1CronFiFees) = BitPackingLib.unpackPairU96(slot3); token0CronFiFeesU96 = t0CronFiFees + evoMem.token0CronFiFeesU96; token1CronFiFeesU96 = t1CronFiFees + evoMem.token1CronFiFeesU96; } /// @notice Get the price oracle data for the pool as of the last virtual order block. /// @return timestamp is the timestamp in seconds when the price oracle was last updated. /// @return token0U256F112 The cumulative price of Token0 measured in amount of /// Token1 * seconds. /// @return token1U256F112 The cumulative price of Token1 measured in amount of /// Token0 * seconds. /// function getPriceOracle() external view override(ICronV1PoolHelpers) returns ( uint256 timestamp, uint256 token0U256F112, uint256 token1U256F112 ) { timestamp = BitPackingLib.unpackOracleTimeStampS2(slot2); token0U256F112 = priceOracle.token0U256F112; token1U256F112 = priceOracle.token1U256F112; } /// @notice Return an array of the order IDs for the specified user _owner. Allows all orders to be fetched /// at once or pagination through the _offset and _maxResults parameters. For instance to get the /// first 100 orderIds of a user, specify _offset=0 and _maxResults=100. To get the second 100 /// orderIds for the same user, specify _offset=100 and _maxResults=100. To get all results at once, /// either specify all known results or 0 for _maxResults. /// @param _owner is the address of the owner to fetch order ids for. /// @param _offset is the number of elements from the end of the list to start fetching results from ( /// consult the operating description above). /// @param _maxResults is the maximum number of results to return when calling this function (i.e. if /// this is set to 1,000 and there are 10,000 results available, only 1,000 from the /// specified offset will be returned). If 0 is specified, then all results available are /// returned. /// @return orderIds A uint256 array of order IDs associated with user's address. /// @return numResults The number of order IDs returned (if a user has less than the specified maximum /// number of results, indices in the returned order ids array after numResults-1 will /// be zero). /// @return totalResults The total number of order IDs associated with this user's address. /// (Useful for pagination of results--i.e. increase _offset by 100 until /// totalResults - 100 is reached). /// function getOrderIds( address _owner, uint256 _offset, uint256 _maxResults ) external view override(ICronV1PoolHelpers) returns ( uint256[] memory orderIds, uint256 numResults, uint256 totalResults ) { uint256[] storage orderIdArr = orderIdMap[_owner]; totalResults = orderIdArr.length; uint256 maxResults = (_maxResults == 0) ? totalResults : _maxResults; orderIds = new uint256[](maxResults); for (uint256 index = _offset; numResults < maxResults && index < totalResults; index++) { orderIds[numResults++] = orderIdArr[index]; } } /// @notice Return the order information of a given order id. /// @param _orderId is the id of the order to return. /// @return order is the order data corresponding to the given order id. See Order struct documentation /// for additional information. /// function getOrder(uint256 _orderId) external view override(ICronV1PoolHelpers) returns (Order memory order) { return virtualOrders.orderMap[_orderId]; } /// @notice Returns the number of virtual orders, Long-Term (LT) swaps, that have been transacted in /// this pool. /// @return nextOrderId The number of virtual orders issued (also the next order ID that is /// assigned to a LT swap.) /// function getOrderIdCount() external view override(ICronV1PoolHelpers) returns (uint256 nextOrderId) { nextOrderId = virtualOrders.nextOrderId; } /// @notice Returns the sales rate of each of the two order pools at the last virtual order block. /// This is the value persisted to state. /// @return salesRate0U112 order pool 0 sales rate. The amount of Token 0 sold to the pool, per block, /// in exchange for Token 1, on behalf of all active Long-Term (LT) swap orders, /// swapping Token0 for Token1, as of the last virtual order block. /// Min. = 0, Max. = (2**112) - 1 /// @return salesRate1U112 order pool 1 sales rate. The amount of Token 1 sold to the pool, per block, /// in exchange for Token 0, on behalf of all active Long-Term (LT) swap orders, /// swapping Token1 for Token0, as of the last virtual order block. /// Min. = 0, Max. = (2**112) - 1 /// function getSalesRates() external view override(ICronV1PoolHelpers) returns (uint256 salesRate0U112, uint256 salesRate1U112) { uint256 salesRates = virtualOrders.orderPools.currentSalesRates; salesRate0U112 = (salesRates >> 112) & C.MAX_U112; salesRate1U112 = salesRates & C.MAX_U112; } /// @notice Get the Last Virtual Order Block (LVOB) for the pool. This is the block number indicating the last block /// where virtual orders have been executed by the pool. If the LVOB is significantly less than the current /// block number, it indicates that the pool has been inactive and that a call to any function that requires /// the execution of virtual orders may incur siginificant gas use. /// @return lastVirtualOrderBlock is the last block number that virtual orders have been executed to. /// function getLastVirtualOrderBlock() external view override(ICronV1PoolHelpers) returns (uint256 lastVirtualOrderBlock) { return virtualOrders.lastVirtualOrderBlock; } /// @notice Get the sales rate ending (per block) at the specified block number. /// @param salesRateEndingPerBlock0U112 the amount of Token 0 per block that will stop being sold /// to the pool after the specified block. /// Min. = 0, Max. = (2**112) - 1 /// @param salesRateEndingPerBlock1U112 the amount of Token 0 per block that will stop being sold /// to the pool after the specified block. /// Min. = 0, Max. = (2**112) - 1 /// @dev NOTE: these values are inserted into state at block numbers divisible by the Order Block /// Interval (OBI)--specifiying block numbers other than those evenly divisible by the /// OBI will result in the returned values being zero. /// function getSalesRatesEndingPerBlock(uint256 _blockNumber) external view override(ICronV1PoolHelpers) returns (uint256 salesRateEndingPerBlock0U112, uint256 salesRateEndingPerBlock1U112) { (salesRateEndingPerBlock0U112, salesRateEndingPerBlock1U112) = BitPackingLib.unpackPairU112( virtualOrders.orderPools.salesRatesEndingPerBlock[_blockNumber] ); } // Slot 1 Access Functions: // //////////////////////////////////////////////////////////////////////////////// /// @notice Gets the current Short-Term (ST) swap fee for the pool in Fee Points. /// @return The ST swap Fee Points (FP). /// Min. = 0, Max. = 1000 (C.MAX_FEE_FP) /// @dev NOTE: Total FP = 100,000. Thus a fee portion is the number of FP out /// of 100,000. /// function getShortTermFeePoints() external view override(ICronV1PoolHelpers) returns (uint256) { return BitPackingLib.unpackU10(slot1, C.S1_OFFSET_SHORT_TERM_FEE_FP); } /// @notice Gets the current Partner swap fee for the pool in Fee Points. /// @return The Partner swap Fee Points (FP). /// Min. = 0, Max. = 1000 (C.MAX_FEE_FP) /// @dev NOTE: Total FP = 100,000. Thus a fee portion is the number of FP out /// of 100,000. /// function getPartnerFeePoints() external view override(ICronV1PoolHelpers) returns (uint256) { return BitPackingLib.unpackU10(slot1, C.S1_OFFSET_PARTNER_FEE_FP); } /// @notice Gets the current Long-Term (LT) swap fee for the pool in Fee Points. /// @return The LT swap Fee Points (FP). /// Min. = 0, Max. = 1000 (C.MAX_FEE_FP) /// @dev NOTE: Total FP = 100,000. Thus a fee portion is the number of FP out /// of 100,000. /// function getLongTermFeePoints() external view override(ICronV1PoolHelpers) returns (uint256) { return BitPackingLib.unpackU10(slot1, C.S1_OFFSET_LONG_TERM_FEE_FP); } /// @notice Gets the amounts of Token0 and Token1 in active virtual orders waiting to /// be sold to the pool as of the last virtual order block. /// @return orders0U112 is the aggregated amount of Token0 for active swaps from Token0 to Token1, /// waiting to be sold to the pool since the last virtual order block. /// Min. = 0, Max. = (2**112)-1 /// @return orders1U112 is the aggregated amount of Token1 for active swaps from Token1 to Token0, /// waiting to be sold to the pool since the last virtual order block. /// Min. = 0, Max. = (2**112)-1 /// function getOrderAmounts() external view override(ICronV1PoolHelpers) returns (uint256 orders0U112, uint256 orders1U112) { (orders0U112, orders1U112) = BitPackingLib.unpackPairU112(slot1); } // Slot 2 Access Functions: // //////////////////////////////////////////////////////////////////////////////// /// @notice Get the proceeds of Token0 and Token1 resulting from /// virtual orders, Long-Term swaps, up to the last virtual order block. /// @return proceeds0U112 is the aggregated amount of Token0 from swaps selling Token1 for Token0 /// to the pool, waiting to be withdrawn, as of the last virtual order block. /// Min. = 0, Max. = (2**112)-1 /// @return proceeds1U112 is the aggregated amount of Token1 from swaps selling Token1 for Token0 /// to the pool, waiting to be withdrawn, as of the last virtual order block. /// Min. = 0, Max. = (2**112)-1 /// function getProceedAmounts() external view override(ICronV1PoolHelpers) returns (uint256 proceeds0U112, uint256 proceeds1U112) { (proceeds0U112, proceeds1U112) = BitPackingLib.unpackPairU112(slot2); } // Slot 3 Access Functions: // //////////////////////////////////////////////////////////////////////////////// /// @notice Gets the current value of the fee shift, which indicates how Long-Term (LT) swap fees are /// split between Cron-Fi and Liquidity Providers (LPs) when Cron-Fi fee collection is enabled. /// @return A value between 1 and 4 that is the fee shift used to determine fee spliting between Cron-Fi /// and LPs: /// /// Fee Shift = 1: /// LP gets 2 fee shares (~66%), Cron-Fi gets 1 fee share (~33%) /// /// Fee Shift = 2: /// LP gets 4 fee shares (~80%), Cron-Fi gets 1 fee share (~20%) /// /// Fee Shift = 3: /// LP gets 8 fee shares (~88%), Cron-Fi gets 1 fee share (~12%) /// /// Fee Shift = 4: /// LP gets 16 fee shares (~94%), Cron-Fi gets 1 fee share (~6%) /// function getFeeShift() external view override(ICronV1PoolHelpers) returns (uint256) { return BitPackingLib.unpackFeeShiftS3(slot3); } /// @notice Gets the amounts of Token0 and Token1 collected as Cron-Fi fees on Long-Term (LT) swaps /// as of the last virtual order block. /// @return cronFee0U96 the amount of Token0 Cron-Fi fees collected as of the last virtual order block. /// Min. = 0, Max. = (2**96) - 1 /// @return cronFee1U96 the amount of Token1 Cron-Fi fees collected as of the last virtual order block. /// Min. = 0, Max. = (2**96) - 1 /// function getCronFeeAmounts() external view override(ICronV1PoolHelpers) returns (uint256 cronFee0U96, uint256 cronFee1U96) { (cronFee0U96, cronFee1U96) = BitPackingLib.unpackPairU96(slot3); } // Slot 4 Access Functions: // //////////////////////////////////////////////////////////////////////////////// /// @notice Use to determine if the pool is collecting Cron-Fi fees currently (Cron-Fi fees are only /// collected on Long-Term swaps if enabled). /// @return True if the pool is collecting Cron-Fi fees, false otherwise. /// function isCollectingCronFees() external view override(ICronV1PoolHelpers) returns (bool) { return BitPackingLib.unpackBit(slot4, C.S4_OFFSET_CRON_FEE_ENABLED) != C.FALSE; } /// @notice Use to determine if the pool is collecting Balancer fees currently (Balancer fees apply to /// any fee collected by the pool--Short and Long Term swaps). /// @return True if the pool is collecting Balancer fees, false otherwise. /// function isCollectingBalancerFees() external view override(ICronV1PoolHelpers) returns (bool) { return BitPackingLib.unpackBit(slot4, C.S4_OFFSET_COLLECT_BALANCER_FEES) != C.FALSE; } /// @notice Get the Balancer Fee charged by the pool. /// @return The current Balancer Fee, a number that is divided by 1e18 (C.ONE_DU1_18) to arrive at a /// fee multiplier between 0 and 1 with 18 fractional decimal digits. /// Min. = 0.000000000000000000, Max. = 1.000000000000000000 /// function getBalancerFee() external view override(ICronV1PoolHelpers) returns (uint256) { return BitPackingLib.unpackBalancerFeeS4(slot4); } /// @notice Gets the amounts of Token0 and Token1 collected as Balancer fees on all swaps as of the last /// virtual order block. /// @return balFee0U96 the amount of Token0 Balancer fees collected as of the last virtual order block. /// Min. = 0, Max. = (2**96) - 1 /// @return balFee1U96 the amount of Token1 Balancer fees collected as of the last virtual order block. /// Min. = 0, Max. = (2**96) - 1 /// function getBalancerFeeAmounts() external view override(ICronV1PoolHelpers) returns (uint256 balFee0U96, uint256 balFee1U96) { (balFee0U96, balFee1U96) = BitPackingLib.unpackPairU96(slot4); } /// @notice Use to determine if the pool's virtual orders are currently paused. If virtual orders are /// paused, the pool will allow Long-Term (LT) swaps to be cancelled and withdrawn from as well /// as liquidity positions to be withdrawn. /// @return True if the pool is paused, false otherwise. /// function isPaused() public view override(ICronV1PoolHelpers) returns (bool) { return BitPackingLib.unpackBit(slot4, C.S4_OFFSET_PAUSE) != C.FALSE; } /// @notice Reverts with error if msg.sender is not the factory owner. /// @dev This internal function is a modifier contract size optimization. /// function _senderIsFactoryOwner() internal view { requireErrCode(msg.sender == ICronV1PoolFactory(FACTORY).owner(), CronErrors.SENDER_NOT_FACTORY_OWNER); } /// @notice Reverts with error if msg.sender is not a pool administrator. /// @dev This internal function is a modifier contract size optimization. /// function _senderIsAdmin() internal view { requireErrCode(adminAddrMap[msg.sender], CronErrors.SENDER_NOT_ADMIN); } /// @notice Reverts with error if msg.sender is not an arbitrage partner. /// @dev This internal function is a modifier contract size optimization. /// function _senderIsArbitragePartner() internal view { requireErrCode(partnerContractAddrMap[msg.sender] != C.NULL_ADDR, CronErrors.SENDER_NOT_ARBITRAGE_PARTNER); } /// @notice Reverts with error if the pool is paused. /// @dev This internal function is a modifier contract size optimization. /// function _poolNotPaused() internal view { requireErrCode(!isPaused(), CronErrors.POOL_PAUSED); } /// @notice Computes the proceeds of a virtual order, Long-Term (LT) swap, for withdrawl or /// cancellation purposes. Proceeds are determined using the staking algorithm, where /// the user's order sales rate, _stakedAmountU128, represents their stake and the /// difference between the normalized proceeds at this juncture or their order end and /// order start are used to calculate their share. The normalized proceeds are /// stored in 128-bits with 64 fractional-bits, hence the scaling down by 64-bits below. /// @param _scaledProceedsU128 The current or order end normalized scaled proceeds value. /// @param _startScaledProceedsU128 The normalized scaled proceeds value at the start of the /// order (or when it was last withdrawn from). /// @param _salesRateU112 The order's sales rate in token per block. /// @param _token0To1 the direction of this swap, true if selling Token 0 for Token1, false otherwise. /// @dev Note explanations for required underflow in this calculation below. /// function _calculateProceeds( uint256 _scaledProceedsU128, uint256 _startScaledProceedsU128, uint256 _salesRateU112, bool _token0To1 ) internal view returns (uint256 proceedsU112) { // NOTE: uint128 casts used here instead of as arguments to this function for gas/size efficiency. // #overUnderFlowIntended // Underflow is required here because it's not the scaled proceeds value but the distance // between them that is important. Underflow maintains the distance relationship. Consult // the Cron-Fi TWAMM Numerical Analysis for a discussion and analysis on this matter. uint256 orderProceedsU128 = uint128(uint128(_scaledProceedsU128) - uint128(_startScaledProceedsU128)); // #unchecked // The multiplication below is not checked for overflow because the product of a U128 and U112 // cannot exceed a U256. (The order proceeds is correct by construction because it is recovered // from a 128-bit storage that checks for 128-bit overflow when stored, the sales rate is known // to be 112-bits or less because Balancer checks the amount in with BAL#526 // BALANCE_TOTAL_OVERFLOW, which is then divided by the number of sales blocks). // // Proceeds is not examined to see if it exceeds MAX_U112 because it is checked downstream from // its use here in the bit packing library's decrementPairU112 function call for underflow on // subtraction from a U112. proceedsU112 = (orderProceedsU128 * _salesRateU112) / (_token0To1 ? ORDER_POOL0_PROCEEDS_SCALING : ORDER_POOL1_PROCEEDS_SCALING); } /// @notice executes existing Virtual Orders (Long-Term-swaps) since last virtual order block, /// updating TWAMM reserve values and other TWAMM state variables up to the specified /// maximum block. /// @param _balance0U112 The Balancer Vault balance of Token 0 for this pool. /// Min. = 0, Max. = (2**112) - 1 /// @param _balance1U112 The Balancer Vault balance of Token 1 for this pool. /// Min. = 0, Max. = (2**112) - 1 /// @param _maxBlock a block to update virtual orders to (useful to specify in emergency situation /// where inactive pool requires too much gas for a single successful call to /// executeVirtualOrders.) If less than or equal to the last virtual order block /// or greater than the current block, the value is set to the current block number. /// @param token0ReserveU112 is the Token 0 reserves of the pool as of _maxBlock. /// Min. = 0, Max. = (2**112) - 1 /// @param token1ReserveU112 is the Token 1 reserves of the pool as of _maxBlock. /// Min. = 0, Max. = (2**112) - 1 /// function _executeVirtualOrders( uint256 _balance0U112, uint256 _balance1U112, uint256 _maxBlock ) private returns (uint256 token0ReserveU112, uint256 token1ReserveU112) { if (!(virtualOrders.lastVirtualOrderBlock < _maxBlock && _maxBlock <= block.number)) { _maxBlock = block.number; } uint256 localSlot4 = slot4; // #savegas ExecVirtualOrdersMem memory evoMem = _getExecVirtualOrdersMem(_balance0U112, _balance1U112); if (!isPaused()) { _executeVirtualOrdersToBlock(evoMem, _maxBlock); // Optimization: // (evoMem.feeShiftU3 != 0) --> cronFeeEnabled == true // // Rather than read from storage again, use the value of feeShiftU3 that was set in // _getExecVirtualOrdersMem based on the cron fee enabled flag (S4_OFFSET_CRON_FEE_ENABLED). // (feeShiftU3 can only take on values 1 through 4 once Cron-Fi fees are enabled, and is 1 // on construction). if (evoMem.feeShiftU3 != 0) { // CAREFUL! Note the change to localSlot4 here!!! It gets set below when balancer fees // are updated. localSlot4 = BitPackingLib.packBit(localSlot4, 0, C.S4_OFFSET_ZERO_CRONFI_FEES); slot3 = BitPackingLib.incrementPairWithClampU96(slot3, evoMem.token0CronFiFeesU96, evoMem.token1CronFiFeesU96); } slot4 = BitPackingLib.incrementPairWithClampU96( localSlot4, evoMem.token0BalancerFeesU96, evoMem.token1BalancerFeesU96 ); // Update order accounting: // slot1 = BitPackingLib.decrementPairU112(slot1, evoMem.token0OrdersU112, evoMem.token1OrdersU112); // Update proceeds accounting: // slot2 = BitPackingLib.incrementPairU112(slot2, evoMem.token0ProceedsU112, evoMem.token1ProceedsU112); // As in UNI V2, the oracle price is based on the reserves before the current operation (i.e. swap). // For TWAMM, we augment that to incorporate the reserves after executing virtual operations. // // NOTE: We only update the oracle when not paused. If paused, pricing info becomes distorted and // shouldn't be incorporated into the oracle. // _updateOracle(evoMem, _maxBlock); } token0ReserveU112 = evoMem.token0ReserveU112; token1ReserveU112 = evoMem.token1ReserveU112; } /// @notice Executes all active virtual orders from the last virtual order block stored /// in state to the specified order block, _blockNumber. The specified order block /// should be greater than the last virtual order block. Writes updates and changes /// to state. /// @param _evoMem aggregated information for gas efficient virtual order exection. See /// documentation in Structs.sol. /// @param _blockNumber is the block number to execute active virtual orders up to from /// the last virtual order block. /// @dev NOTE: Total FP = 100,000. Thus a fee portion is the number of FP out of 100,000. /// function _executeVirtualOrdersToBlock(ExecVirtualOrdersMem memory _evoMem, uint256 _blockNumber) private { uint256 poolFeeLTFP = BitPackingLib.unpackU10(slot1, C.S1_OFFSET_LONG_TERM_FEE_FP); (LoopMem memory loopMem, uint256 expiryBlock) = _getLoopMem(); // Loop through active virtual orders preceeding the final block interval, performing // aggregate Long-Term (LT) swaps and updating sales rates in memory and scaled proceeds // in state. // uint256 prevLVOB = loopMem.lastVirtualOrderBlock; while (expiryBlock < _blockNumber) { _executeVirtualTradesAndOrderExpiries(_evoMem, expiryBlock, loopMem, poolFeeLTFP); _calculateOracleIncrement(_evoMem, prevLVOB, expiryBlock); prevLVOB = loopMem.lastVirtualOrderBlock; // Handle orders expiring at end of interval _decrementSalesRates(expiryBlock, loopMem); _storeScaledProceeds(loopMem, expiryBlock); expiryBlock += ORDER_BLOCK_INTERVAL; } // Process the active virtual orders of the final block interval, performing // aggregate Long-Term (LT) swaps and updating sales rates in memory and scaled proceeds // in state. // if (loopMem.lastVirtualOrderBlock != _blockNumber) { expiryBlock = _blockNumber; _executeVirtualTradesAndOrderExpiries(_evoMem, expiryBlock, loopMem, poolFeeLTFP); _calculateOracleIncrement(_evoMem, prevLVOB, _blockNumber); // Handle orders expiring at end of interval _decrementSalesRates(expiryBlock, loopMem); _storeScaledProceeds(loopMem, expiryBlock); } // Update virtual order and order pool state from values in memory: // virtualOrders.lastVirtualOrderBlock = loopMem.lastVirtualOrderBlock; virtualOrders.orderPools.currentSalesRates = BitPackingLib.packPairU112( 0, loopMem.currentSalesRate0U112, loopMem.currentSalesRate1U112 ); virtualOrders.orderPools.scaledProceeds = BitPackingLib.packPairU128( loopMem.scaledProceeds0U128, loopMem.scaledProceeds1U128 ); } /// @notice Stores the scaled proceeds contained in _loopMem in state at the given block number, /// _expiryBlock. /// @param _loopMem aggregated information for gas efficient virtual order loop execution. See /// documentation in Structs.sol. /// @param _expiryBlock is the next block upon which virtual orders expire. It is aligned on /// multiples of the order block interval. /// function _storeScaledProceeds(LoopMem memory _loopMem, uint256 _expiryBlock) private { virtualOrders.scaledProceedsAtBlock[_expiryBlock] = BitPackingLib.packPairU128( _loopMem.scaledProceeds0U128, _loopMem.scaledProceeds1U128 ); } /// @notice Update the price oracle on the first transaction within a block (Uniswap V2 style Price /// Oracle adapted for TWAMM). /// @param _evoMem aggregated information for gas efficient virtual order exection. See /// documentation in Structs.sol. /// @param _updateBlock is the block number specified in the calling function. This may be less /// than the current block number (for example if executeVirtualOrdersToBlock /// is called with a number less than the current block number to free up /// excess gas load from inactivity). /// function _updateOracle(ExecVirtualOrdersMem memory _evoMem, uint256 _updateBlock) private { (uint32 timeElapsed, uint32 blockTimeStamp) = _getOracleTimeData(_updateBlock); if (timeElapsed > 0) { // #overUnderFlowIntended // The UQ256x112 incremented result relies upon and expects overflow // and is unchecked. The reason is that it is the difference between // price oracle samples over time that matters, not the absolute value. // As noted above, see the Uniswap V2 whitepaper. priceOracle.token0U256F112 += _evoMem.token0OracleU256F112; priceOracle.token1U256F112 += _evoMem.token1OracleU256F112; } slot2 = BitPackingLib.packOracleTimeStampS2(slot2, blockTimeStamp); } /// @notice Trigger Balancer Vault re-entrancy check for read-only re-entrancy vulnerability /// described here: /// /// https://forum.balancer.fi/t/reentrancy-vulnerability-scope-expanded/4345 /// @dev Balancer describes this workaround as follows: /// /// '... this is the cheapest way to trigger a reentrancy check on the Vault. In /// short, it does nothing. The code "withdraws" an amount of 0 of token address(0) /// from the Vault's internal balance for the calling contract (the pool) and sends /// it to address(0).' /// function _triggerVaultReentrancyCheck() private { IVault.UserBalanceOp[] memory ops = new IVault.UserBalanceOp[](1); ops[0].kind = IVault.UserBalanceOpKind.WITHDRAW_INTERNAL; ops[0].sender = address(this); VAULT.manageUserBalance(ops); } /// @notice Execute a Short-Term (ST) swap atomically in a single block on one token for the other as /// described in the parameters below. /// @param _token0To1 the direction of this swap, true if selling Token 0 for Token1, false otherwise. /// @param _regularSwapType is true if the swap is SwapType.RegularSwap, false if it is /// SwapType.PartnerSwap. /// @param _amountInU112 is the amount of token being sold to the pool in this swap. /// Min. = 0, Max. = (2**112) - 1 /// @param _token0ReserveU112 is the current Token 0 reserves of the pool. /// Min. = 0, Max. = (2**112) - 1 /// @param _token1ReserveU112 is the current Token 1 reserves of the pool. /// Min. = 0, Max. = (2**112) - 1 /// @return amountOutU112 is the amount of token being bought from the pool in this swap. /// Min. = 0, Max. = (2**112) - 1 function _shortTermSwap( bool _token0To1, bool _regularSwapType, uint256 _amountInU112, uint256 _token0ReserveU112, uint256 _token1ReserveU112 ) private returns (uint256 amountOutU112) { uint256 swapFeeU10 = BitPackingLib.unpackU10( slot1, _regularSwapType ? C.S1_OFFSET_SHORT_TERM_FEE_FP : C.S1_OFFSET_PARTNER_FEE_FP ); // #unchecked // Balancer confirms the amount in does not overflow the pool (BAL#526 // BALANCE_TOTAL_OVERFLOW), thus the multiply of _amountInU112 and a U10 // cannot overflow a U256. uint256 grossFee = (_amountInU112 * swapFeeU10).divDown(C.TOTAL_FP); uint256 localSlot4 = slot4; // #savegas uint256 collectBalancerFees = BitPackingLib.unpackBit(localSlot4, C.S4_OFFSET_COLLECT_BALANCER_FEES); if (collectBalancerFees != C.FALSE) { uint256 balancerFeeDU1F18 = BitPackingLib.unpackBalancerFeeS4(localSlot4); // #unchecked // Multiplication of the grossFee (which at most could be U122 as described // above--and is much less because of division by C.TOTAL_FP, i.e. U106) by // balancerFeeDU1F18, a U60 at most, results in U166 to U182 at worst which // will not overflow a U256. Further mitigated with the division by 1e18 (U59). uint256 balancerFeeU112 = (grossFee * balancerFeeDU1F18).divDown(C.DENOMINATOR_DU1_18); // #contractsize add balancer fees from swap slot4 = BitPackingLib.incrementPairWithClampU96( localSlot4, (_token0To1 ? balancerFeeU112 : 0), (_token0To1 ? 0 : balancerFeeU112) ); } // NOTE: LP Fees are automatically collected in the vault balances and need not be calculated here. // #unchecked // Subtraction unchecked below because grossFee is a fraction of _amountInU112 and // cannot underflow. // IMPORTANT: divDown in computing the grossFee above essential to prevent underflow // zero amount in attack. uint256 amountInLessFeesU112 = _amountInU112 - grossFee; // #unchecked // Balancer confirms the amount in does not overflow the pool (BAL#526 // BALANCE_TOTAL_OVERFLOW), thus the multiply of _amountInU112 and a value, // _tokenNReserve, less than the value confirmed to not overflow will // not exceed the U256 container (worst case U113). uint256 nextTokenInReserveU112 = ( /* token-in reserve: */ _token0To1 ? _token0ReserveU112 : _token1ReserveU112 ) + amountInLessFeesU112; // #unchecked // Because of Balancer Vault overflow checks and the way the token reserves // are computed from known U112 maximum values, the multiplication below is // unchecked as the product of two U112 values is not going to overflow the // U256 container. amountOutU112 = (( /* token-out reserve: */ _token0To1 ? _token1ReserveU112 : _token0ReserveU112 ) * amountInLessFeesU112).divDown(nextTokenInReserveU112); } /// @notice Execute a Long-Term swap (LT) Virtual Order, which sells an amount of one of the pool /// tokens to the pool over a number of blocks, as described in the parameters below. /// An LT swap can be withdrawn one or more times until the order has expired and all proceeds /// have been withdrawn. /// An LT swap can be cancelled anytime before expiry for a refund of unsold tokens and any /// proceeds obtained before cancellation. /// To withdraw or cancel, call the IVault.exit function (see documentaion for onExitPool). /// WARNING: Any amount specified that does not divide evenly into the amount of blocks for /// the virtual order will end up in pool reserves, yielding the user nothing for their capital. /// For instance a trade of 224 Token-0 over two intervals (OBI=75) starting at block 75 would /// result in a sales rate of 2 Token-0 per block for 150 blocks, 2 Order Block Intervals (OBI), /// and the remaining 74 Token-0 would end up in the pool reserves yielding no benefit to the user. /// @param _sender is the account issuing the LT swap transaction. Only this account can withdraw the /// order. /// @param _delegate is an account that is able to withdraw or cancel the LT swap on behalf of the /// sender account, as long as the recipient specified is the sender account. /// If the delegate is set to the sender account, then the delegate is set /// to the null address. /// @param _token0To1 is the direction of this swap, true if selling Token 0 for Token1, false otherwise. /// @param _amountInU112 is the amount of token being sold to the pool in this swap. /// Min. = 0, Max. = (2**112) - 1 /// @param _orderIntervals is the number of intervals to execute the LT swap before expiring. An interval /// can be 75 blocks (Stable Pool), 300 blocks (Liquid Pool) or 1200 blocks (Volatile /// Pool). /// Min. = 0, Max. = STABLE_MAX_INTERVALS, /// LIQUID_MAX_INTERVALS, /// VOLATILE_MAX_INTERVALS (depending on POOL_TYPE). /// @return orderId is the order identifier for the LT swap virtual order created. Users can see what /// order identifiers they've created by callin getOrderIds. /// @dev The order id for an issued order is in the event log emitted by this function. No safety is provided /// for checking existing order ids being reissued because the order id space is very large: (2**256) - 1. /// /// #RISK: An order-wrap attack (where a user trys to issue so many orders that the order ID counter /// overflows, giving them access to order proceeds) is unlikely since the minimum measured gas /// cost of a call to onSwap resulting in a call to _longTermSwap is ~220k gas (not including /// approvals and transfers). /// function _longTermSwap( address _sender, address _delegate, bool _token0To1, uint256 _amountInU112, uint256 _orderIntervals ) private returns (uint256 orderId) { // #RISK: The accumulation of scaled proceeds works based on the difference between the proceeds // at collection and the proceeds at submission. Underflow is supported when subtracting these // values, however, there is a risk that an order goes so long that the proceeds overflow twice. // It's an unlikely risk that occurs in pools with extremely asymmetric reserves, but this // maximum order length serves to reduce that risk. requireErrCode(_orderIntervals <= MAX_ORDER_INTERVALS, CronErrors.MAX_ORDER_LENGTH_EXCEEDED); // Determine selling rate based on number of blocks to expiry and total amount: // // #unchecked: // Subtraction in assignment of lastExpiryBlock won't underflow because the // block number will always be greater than the ORDER_BLOCK_INTERVAL on mainnet until // it overflows in a very long time (suspect the universe may have collapsed by then). // // Overflow not possible in the multiplication and addition of the assignment of // orderExpiry in a reasonable time (i.e. universe may collapse first). Reason is // that max(_orderIntervals) = STABLE_MAX_INTERVALS (18-bits), max(ORDER_BLOCK_INTERVAL) = // VOLATILE_OBI (16-bits), which the product of maxes at 34-bits. Thus lastExpiryBlock // is the dominant term when the block.number approaches (2**256 - 1) - (2**34 - 1). // // Underflow not possible in assignment to tradeBlocks since orderExpiry always > // block.number. uint256 lastExpiryBlock = block.number - (block.number % ORDER_BLOCK_INTERVAL); uint256 orderExpiry = ORDER_BLOCK_INTERVAL * (_orderIntervals + 1) + lastExpiryBlock; // +1 protects from div 0 uint256 tradeBlocks = orderExpiry - block.number; uint256 sellingRateU112 = _amountInU112 / tradeBlocks; // Intended: Solidity rounds towards zero. requireErrCode(sellingRateU112 > 0, CronErrors.ZERO_SALES_RATE); // Update the current and ending sales rates state: // // NOTE: re-using pair increment methods with single value for #contractsize reduction. OrderPools storage ops = virtualOrders.orderPools; ops.currentSalesRates = BitPackingLib.incrementPairU112( ops.currentSalesRates, _token0To1 ? sellingRateU112 : 0, _token0To1 ? 0 : sellingRateU112 ); ops.salesRatesEndingPerBlock[orderExpiry] = BitPackingLib.incrementPairU112( ops.salesRatesEndingPerBlock[orderExpiry], _token0To1 ? sellingRateU112 : 0, _token0To1 ? 0 : sellingRateU112 ); // NOTE: Cast to uint112 below for sellingRateU112 is safe because _amountInU112 must be less than 112-bits // as checked by Balancer and reverted with Balancer error BAL#526 BALANCE_TOTAL_OVERFLOW. // NOTE: Cast to uint128 below for unpackPairU128 is safe because the result of that function // cannot execeed 128-bits (correct by construction, i.e. function pulls a 128-bit value from // packed storage in 256-bit slot). // NOTE: scaledProceeds0 is measured in amount of Token 1 received for selling Token 0 from Order Pool 0 // to the AMM (and vice-versa scaledProceeds1). // // #overUnderFlowIntended // In the very distant future, the nextOrderId will overflow. Given the 5 year // order length limit, it's improbable that 2**256 -1 orders will occur and // result in the overwriting of an existing order: // // * Assume 12s blocks. // * Five years in seconds 157788000 (365.25d / year). // * Blocks in 5 years = 13149000. // * Conservatively overestimate 1000 orders per block, // then 13149000000 orders in 5 years. // * 34-bits used for 5 years, assuming an excessive number of orders per block. // * Caveat Emptor on those who do not withdraw their proceeds in // an insufficient amount of time from order start. orderId = virtualOrders.nextOrderId++; virtualOrders.orderMap[orderId] = Order( _token0To1, uint112(sellingRateU112), uint128(BitPackingLib.unpackU128(ops.scaledProceeds, _token0To1)), _sender, ((_sender != _delegate) ? _delegate : C.NULL_ADDR), orderExpiry ); orderIdMap[_sender].push(orderId); // Update order accounting (add the user's order amount to the global orders accounting): // // NOTE: The amount in (_amountInU112) is not the amount that will be swapped due to // truncation. The amount that will be swapped is the number of blocks in the order // multiplied by the order sales rate. The remainder is implicitly added to the pool // reserves, augmenting LP rewards (Caveat Emptor Design Philosophy for Swap User). // // #unchecked // Multiplication below is unchecked because sellingRate is maximum U112 and // tradeBlocks is maximum U24 (STABLE_OBI * STABLE_MAX_INTERVALS = 13149000 blocks), // the product of which will not overflow a U256. Also note the U112 limit is checked // in the increment call. uint256 orderAmount = sellingRateU112 * tradeBlocks; slot1 = BitPackingLib.incrementPairU112(slot1, (_token0To1 ? orderAmount : 0), (_token0To1 ? 0 : orderAmount)); } /// @notice Withdraws any collected Cron-Fi fees from the pool and resets the pool accounting of /// Cron-Fi fees to zero for both Token0 and Token1. /// @param _sender must be the current fee address. /// @return token0OutU96 Amount of Token0 collected as Cron-Fi fees in Long-Term (LT) swaps. /// Min. = 0, Max = (2**96) - 1 /// @return token1OutU96 Amount of Token1 collected as Cron-Fi fees in LT swaps. /// Min. = 0, Max = (2**96) - 1 /// function _withdrawCronFees(address _sender) private returns (uint256 token0OutU96, uint256 token1OutU96) { // IMPORTANT: safety check to ensure user calling this is fee address: requireErrCode(feeAddr == _sender, CronErrors.SENDER_NOT_FEE_ADDRESS); // Optimization: // The U96 Cron-Fi fees below are assigned to the output tokens in onExitPool and // the pool's Cron-Fi fee counters in slot3 are cleared. // A flag indicating they have been cleared in slot4 is also set. This reduces the // gas used in calculating pool reserves for subsequent transactions (why read slot3 // if the values within are zero?). See function _calculateReserves for the use of // this flag in slot4. // The optimization is re-used here too (why read the Cron-Fi fees from slot3 // if they are zero). uint256 localSlot4 = slot4; // #savegas requireErrCode( BitPackingLib.unpackBit(localSlot4, C.S4_OFFSET_ZERO_CRONFI_FEES) == C.FALSE, CronErrors.NO_FEES_AVAILABLE ); // Send the Cron-Fi fees out of the pool and zero our accounting of them: (slot3, token0OutU96, token1OutU96) = BitPackingLib.unpackAndClearPairU96(slot3); // Set the Zero Cron-Fi fees flag since we've cleared the Cron-Fi fees counters: slot4 = BitPackingLib.packBit(localSlot4, 1, C.S4_OFFSET_ZERO_CRONFI_FEES); emit FeeWithdraw(_sender, token0OutU96, token1OutU96); } /// @notice A wrapper around _withdrawLongTermSwap to mitigate stack depth limitations in that method. /// See documentation of _withdrawLongTermSwap for more information. /// @param _orderId the order id to withdraw or cancel. /// @param _sender is the account performing the LT swap order withdrawl or cancellation. /// For long term orders, a "delegate" may be specified, this address is able to /// perform LT order withdraws and cancellations on behalf of the LT swap owner as /// long as the recipient is the LT swap owner. /// @param _recipient the recipient must always be the original order owner (the address /// that issued the order) if a "delegate" address is performing the withdrawl or /// cancellation. If the order owner is performing the withdrawl or cancellation, the /// recipient can be set to whatever destination address desired. /// @param _cancel if true, cancel the order, otherwise if false, withdraw the order. /// @return token0OutU112 the amount of Token 0 remitted from the order as either proceeds or a refund. /// Min. = 0, Max. = (2**112)-1 /// @return token1OutU112 the amount of Token 1 remitted from the order as either proceeds or a refund. /// Min. = 0, Max. = (2**112)-1 /// function _withdrawLongTermSwapWrapper( uint256 _orderId, address _sender, address _recipient, bool _cancel ) private returns (uint256 token0OutU112, uint256 token1OutU112) { Order storage order = virtualOrders.orderMap[_orderId]; requireErrCode(order.owner != C.NULL_ADDR, CronErrors.CLEARED_ORDER); if (_sender != order.owner) { requireErrCode(_recipient == order.owner, CronErrors.RECIPIENT_NOT_OWNER); } requireErrCode( (_sender == order.owner || _sender == order.delegate) && _sender != C.NULL_ADDR, CronErrors.SENDER_NOT_ORDER_OWNER_OR_DELEGATE ); (bool token0To1, uint256 refundU112, uint256 proceedsU112) = _withdrawLongTermSwap(order, _cancel); emit WithdrawLongTermSwap( order.owner, (_cancel) ? ((token0To1) ? address(TOKEN0) : address(TOKEN1)) // sell token : C.NULL_ADDR, refundU112, (token0To1) ? address(TOKEN1) : address(TOKEN0), // buy token proceedsU112, _orderId, _sender ); // NOTE: The execute virtual orders run prior to this function in function onExitPool updates the // orders and proceeds amounts. token0OutU112 = (token0To1) ? refundU112 : proceedsU112; token1OutU112 = (token0To1) ? proceedsU112 : refundU112; } /// @notice This function performs a withdraw or a cancel of a Long-Term (LT) swap virtual order, which /// remits proceeds and if cancelling, also refunds remaining order amounts. /// Funds may be withdrawn from an order multiple times up until order expiry and the last of /// the proceeds are withdrawn. /// Orders may be cancelled up to order expiry. Both proceeds and remaining order funds will be /// remitted upon cancellation. /// @param _order information of a specific LT swap order. See Order struct documentation in Structs.sol. /// @param _cancel indicates whether the virtual order is to be cancelled if true. If false, the order /// proceeds are to be withdrawn. /// @return token0To1 is the direction of this LT swap. It is true if selling Token 0 for Token1, false /// otherwise. /// @return refundU112 is the amount of token to be refunded. /// Min. = 0, Max. = (2**112) - 1 /// @return proceedsU112 is the amount of token already purchased in the LT swap to be remitted. /// Min. = 0, Max. = (2**112) - 1 /// function _withdrawLongTermSwap(Order storage _order, bool _cancel) private returns ( bool token0To1, uint256 refundU112, uint256 proceedsU112 ) { token0To1 = _order.token0To1; uint256 expiryBlock = _order.orderExpiry; uint256 salesRateU112 = _order.salesRate; // NOTE: There is no need to check if order is completed using orderExpiry // or salesRate, because entire Order struct is zeroed/falsed on cancel // or final withdraw. A 2nd cancel or withdraw attempt will revert with // CronErrors.SENDER_NOT_ORDER_OWNER_OR_DELEGATE // #RISK: Although the NULL_ADDR technically owns zeroed cancelled orders, // anyone owning that address would be able to withdraw nothing // because the salesRateU112 (order.salesRate) is zerod after // cancel. if (_cancel) { // Calculate unsold amount to refund: // // NOTE: Must use last virtual order block, not block.number to yield correct result // when pool is paused. // // #unchecked // The subtraction is unchecked because the error check confirms that expiryBlock // is larger than lastVirtualOrderBlock, preventing underflow. // // The multiplication is unchecked for overflow because the sales rate is // computed from the amount in, which is checked by Balancer (BAL#526 // BALANCE_TOTAL_OVERFLOW) and known to be less than U112. The maximum // difference between the expiry block and last virtual order block is // constrained to be U18 (the MAX_ORDER_INTERVALS largest value, // STABLE_MAX_INTERVALS), the product of which cannot exceed U130. requireErrCode(expiryBlock > virtualOrders.lastVirtualOrderBlock, CronErrors.CANT_CANCEL_COMPLETED_ORDER); refundU112 = (expiryBlock - virtualOrders.lastVirtualOrderBlock) * salesRateU112; } uint256 scaledProceedsU128 = BitPackingLib.unpackU128( (!_cancel && (block.number > expiryBlock)) ? virtualOrders.scaledProceedsAtBlock[expiryBlock] // Expired or cancelled order - calculate scaled proceeds at expiryBlock. : virtualOrders.orderPools.scaledProceeds, // Unexpired order. Remit current proceeds. token0To1 ); proceedsU112 = _calculateProceeds( scaledProceedsU128, _order.scaledProceedsAtSubmissionU128, salesRateU112, token0To1 ); // CAREFUL: This must come after the calculation of proceedsU112 if (!_cancel && (block.number <= expiryBlock)) { // Reset stake (sales rate) if not cancellation and not expired order // NOTE: Cast to uint128 below for scaledProceedsAtSubmissionU128 is safe because result of function unpackPairU128 // cannot execeed 128-bits (correct by construction, i.e. function pulls a 128-bit value from // packed storage in 256-bit slot, value is checked for overflow when it was stored). _order.scaledProceedsAtSubmissionU128 = uint128(scaledProceedsU128); } requireErrCode(refundU112 > 0 || proceedsU112 > 0, CronErrors.NO_FUNDS_AVAILABLE); if (_cancel) { OrderPools storage ops = virtualOrders.orderPools; // Decrement the current and ending sales rates: ops.currentSalesRates = BitPackingLib.decrementPairU112( ops.currentSalesRates, token0To1 ? salesRateU112 : 0, token0To1 ? 0 : salesRateU112 ); ops.salesRatesEndingPerBlock[expiryBlock] = BitPackingLib.decrementPairU112( ops.salesRatesEndingPerBlock[expiryBlock], token0To1 ? salesRateU112 : 0, token0To1 ? 0 : salesRateU112 ); // Update the order accounting: slot1 = BitPackingLib.decrementPairU112(slot1, (token0To1 ? refundU112 : 0), (token0To1 ? 0 : refundU112)); } // Update the proceed accounting: slot2 = BitPackingLib.decrementPairU112(slot2, (token0To1 ? 0 : proceedsU112), (token0To1 ? proceedsU112 : 0)); if (_cancel || block.number > expiryBlock) { // Remove stake (sales rate) and clear order state for rebate and to indicate order complete: _order.token0To1 = false; _order.salesRate = 0; _order.scaledProceedsAtSubmissionU128 = 0; _order.owner = C.NULL_ADDR; _order.delegate = C.NULL_ADDR; _order.orderExpiry = 0; } } /// @notice This function performs a join given the amounts of Token 0 and Token 1 to add to the pool /// along with the current virtual reserves. /// @param _recipient is the account designated to receive pool shares in the form of LP tokens when /// Joining the pool. Can be set to _sender if sender wishes to receive the tokens /// and Join Events. /// @param _token0InU112 is the amount of Token 0 to Join the pool with. /// Min. = 0, Max. = (2**112) - 1 /// @param _token1InU112 is the amount of Token 1 to Join the pool with. /// Min. = 0, Max. = (2**112) - 1 /// @param _token0ReserveU112 is the current Token 0 reserves of the pool. /// Min. = 0, Max. = (2**112) - 1 /// @param _token1ReserveU112 is the current Token 1 reserves of the pool. /// Min. = 0, Max. = (2**112) - 1 /// @return amountLP is the amount of Liquidity Provider (LP) tokens returned in exchange for the /// amounts of Token 0 and Token 1 provided to the pool. function _join( address _recipient, uint256 _token0InU112, uint256 _token1InU112, uint256 _token0ReserveU112, uint256 _token1ReserveU112 ) private returns (uint256 amountLP) { requireErrCode(_recipient != C.NULL_ADDR, CronErrors.NULL_RECIPIENT_ON_JOIN); uint256 supplyLP = totalSupply(); if (supplyLP == 0) { requireErrCode( (_token0InU112 > C.MINIMUM_LIQUIDITY) && (_token1InU112 > C.MINIMUM_LIQUIDITY), CronErrors.INSUFFICIENT_LIQUIDITY ); // #unchecked // The check of _token0InU112 and _token1InU112 ensure that both are less // than 112-bit maximums. Multiplying two 112-bit numbers will not exceed // 224-bits, hence no checked mul operator here. // The check that both _token0InU112 and _token1InU112 exceed // C.MINIMUM_LIQUIDITY, means that the square root of their product cannot // be less than C.MINIMUM_LIQUIDITY, hence no checked sub operator here. amountLP = sqrt(_token0InU112 * _token1InU112) - C.MINIMUM_LIQUIDITY; // Initial Join / Mint does not execute virtual orders, is only run once b/c of MINIMUM_LIQUIDITY, // thus a call to update the oracle resolves to simply setting the oracle timestamp: slot2 = BitPackingLib.packOracleTimeStampS2(slot2, block.timestamp); _mintPoolTokens(address(0), C.MINIMUM_LIQUIDITY); // Permanently locked for div / 0 safety. // Set the last virtual order block to the block number. This reduces the gas used // in virtual order execution iterations for the first call to execute virtual orders // now that the pool has liquidity. virtualOrders.lastVirtualOrderBlock = block.number; } else { amountLP = Math.min( _token0InU112.mul(supplyLP).divDown(_token0ReserveU112), _token1InU112.mul(supplyLP).divDown(_token1ReserveU112) ); } requireErrCode(amountLP > 0, CronErrors.INSUFFICIENT_LIQUIDITY); _mintPoolTokens(_recipient, amountLP); } /// @notice This function performs an exit given the amount of Liquidity Provider (LP) tokens /// to return to the pool along with the current virtual reserves. /// @param _sender the account performing the liquidity removal. /// @param _tokensLP the number of LP tokens to redeem in exchange for the pool's asset tokens. /// @param _token0ReserveU112 is the current Token 0 reserves of the pool. /// Min. = 0, Max. = (2**112) - 1 /// @param _token1ReserveU112 is the current Token 1 reserves of the pool. /// Min. = 0, Max. = (2**112) - 1 /// @return token0OutU112 is the amount of Token 0 returned by the pool in exchange for LP tokens. /// Min. = 0, Max. = (2**112) - 1 /// @return token1OutU112 is the amount of Token 1 returned by the pool in exchange for LP tokens. /// Min. = 0, Max. = (2**112) - 1 /// function _exit( address _sender, uint256 _tokensLP, uint256 _token0ReserveU112, uint256 _token1ReserveU112 ) private returns (uint256 token0OutU112, uint256 token1OutU112) { // CAREFUL: Next line must preceed burning of pool liquidity provider tokens. uint256 lTotalSupply = totalSupply(); _burnPoolTokens(_sender, _tokensLP); token0OutU112 = (_token0ReserveU112.mul(_tokensLP)).divDown(lTotalSupply); token1OutU112 = (_token1ReserveU112.mul(_tokensLP)).divDown(lTotalSupply); emit PoolExit(_sender, _tokensLP, token0OutU112, token1OutU112); } /// @notice Handles the updating of the stored Balancer fee multiplier and remittance of any collected /// Balancer fees in onJoinPool and onExitPool function calls. /// @param _protocolFeeDU1F18 the newest Balancer Fee passed into the pool by the onJoinPool and /// onExitPool functions. This number is divided by 1e18 (C.ONE_DU1_18) to /// arrive at a fee multiplier between 0 and 1, inclusive, with 18 /// fractional decimal digits. /// Min. = 0, Max. = 10**18 /// @return dueProtocolFeeAmountsU96 the amount of Token 0 and Token 1 collected by the pool for /// Balancer. Values are returned in the same array ordering that /// IVault.getPoolTokens returns. /// Min. = 0, Max. = (2**96) - 1 /// function _handleBalancerFees(uint256 _protocolFeeDU1F18) private returns (uint256[] memory dueProtocolFeeAmountsU96) { uint256 localSlot4 = slot4; if (_protocolFeeDU1F18 <= C.ONE_DU1_18) { uint256 currentBalancerFee = BitPackingLib.unpackBalancerFeeS4(localSlot4); // #savegas: check before write if (currentBalancerFee != _protocolFeeDU1F18) { localSlot4 = BitPackingLib.packBalancerFeeS4(localSlot4, _protocolFeeDU1F18); } } else { // Ignore change and keep operating with old fee if new fee is too large but notify through logs. emit ProtocolFeeTooLarge(_protocolFeeDU1F18); } // Send the Balancer fees out of the pool and zero our accounting of them: dueProtocolFeeAmountsU96 = new uint256[](2); (slot4, dueProtocolFeeAmountsU96[C.INDEX_TOKEN0], dueProtocolFeeAmountsU96[C.INDEX_TOKEN1]) = BitPackingLib .unpackAndClearPairU96(localSlot4); } /// @notice Returns the TWAMM pool's reserves after the non-stateful execution of all virtual orders /// up to the current block. /// IMPORTANT - This function does not meaningfully modify state despite the lack of a "view" /// designator for state mutability. (The call to _triggerVaultReentrancyCheck /// Unfortunately prevents the "view" designator as meaningless value is written /// to state to trigger a reentracy check). /// Runs virtual orders from the last virtual order block up to the current block to provide /// visibility into the current accounting for the pool. /// If the pool is paused, this function reflects the accounting values of the pool at /// the last virtual order block (i.e. it does not execute virtual orders to deliver the result). /// @param _maxBlock a block to update virtual orders to. If less than or equal to the last virtual order /// block or greater than the current block, the value is set to the current /// block number. /// @param _paused is true to indicate the result should be returned as though the pool is in a paused /// state where virtual orders are not executed and only withdraw, cancel and liquidations /// are possible (check function isPaused to see if the pool is in that state). If false /// then the virtual reserves are computed from virtual order execution to the specified /// block. /// @return evoMem a struct containing the pool's virtual reserves for Token 0 and Token 1 along with /// the changes to the Orders, Proceeds, and Fee accounting to the current state values /// for Token 0 and Token 1. See the documentation of struct ExecVirtualOrdersMem for /// details. /// IMPORTANT Note above that struct values for token0ReserveU112 and token1ReserveU112 /// are the virtual values of the reserves at the current block, whereas other /// values in the ExecVirtualOrdersMem struct are the DIFFERENCES from the /// state of their respective values and their virtual values. /// @return blockNumber The block that the virtual reserve values were computed at. Should /// match parameter _maxBlock, unless _maxBlock was not greater than the /// last virtual order block or less than or equal to the current block. /// function _getVirtualReserves(uint256 _maxBlock, bool _paused) private returns (ExecVirtualOrdersMem memory evoMem, uint256 blockNumber) { blockNumber = (virtualOrders.lastVirtualOrderBlock < _maxBlock && _maxBlock <= block.number) ? _maxBlock : block.number; _triggerVaultReentrancyCheck(); (, uint256[] memory balancesU112, ) = VAULT.getPoolTokens(POOL_ID); evoMem = _getExecVirtualOrdersMem(balancesU112[C.INDEX_TOKEN0], balancesU112[C.INDEX_TOKEN1]); if (!_paused) { _executeVirtualOrdersView(evoMem, blockNumber); } } /// @notice Executes all virtual orders between current lastVirtualOrderBlock and blockNumber /// also handles orders that expire at end of final block. This assumes that no orders /// expire inside the given interval. /// /// @param _evoMem aggregated information for gas efficient virtual order execution. See /// documentation in Structs.sol. /// @param _expiryBlock is the next block upon which virtual orders expire. It is aligned on /// multiples of the order block interval. /// @param _loopMem aggregated information for gas efficient virtual order loop execution. See /// documentation in Structs.sol. /// @param _poolFeeLTFP is the pool fee charged for long term swaps in Fee Points (FP). /// @dev NOTE: Total FP = 100,000. Thus a fee portion is the number of FP out of 100,000. /// function _executeVirtualTradesAndOrderExpiries( ExecVirtualOrdersMem memory _evoMem, uint256 _expiryBlock, LoopMem memory _loopMem, uint256 _poolFeeLTFP ) private view { // Determine how many blocks are in the current interval and compute // the amount of Token0 and Token1 being sold to the pool and any // fees. (Fees are collected as a percentage of tokens being sold to // the pool as opposed to tokens bought from the pool). // // #unchecked // Subtraction below is unchecked because _expiryBlock is // always greater than the lastVirtualOrderBlock by at least // 1, and up to a block interval (see function _getLoopMem // where it's initialized). uint256 intervalBlocks = _expiryBlock - _loopMem.lastVirtualOrderBlock; uint256 currentSalesRate0U112 = _loopMem.currentSalesRate0U112; // #savegas uint256 currentSalesRate1U112 = _loopMem.currentSalesRate1U112; // #savegas // #unchecked // The computation of token0In and token1In are not checked // for overflow. The current sales rates will not exceed MAX_U112 // and the number of interval blocks is bounded to a maximum of // 1200 (VOLATILE_OBI), well within the safe range of MAX_U256. // // Likewise calculation of grossFeeT0 and grossFeeT1 are not // checked for overflow because token0In has a maximum of MAX_U123 // and it's multiplied by _poolFeeLTFP which has a maximum // of 1000 (MAX_FEE_FP), well within the safe range of MAX_U256. // // grossFeeT0 and grossFeeT1 thus have a practical maximum value of // MAX_U112 * 1200 * 1000 / 100000 ---> 116-bits // // NOTE: division with rounding up is performed to favor the pool. uint256 token0In = currentSalesRate0U112 * intervalBlocks; uint256 grossFeeT0 = (token0In * _poolFeeLTFP).divUp(C.TOTAL_FP); // uint256 token1In = currentSalesRate1U112 * intervalBlocks; uint256 grossFeeT1 = (token1In * _poolFeeLTFP).divUp(C.TOTAL_FP); (uint256 lpFeeT0, uint256 lpFeeT1) = _updateFees(_evoMem, grossFeeT0, grossFeeT1); // Compute the amount of each token bought from the pool for the current // interval. Then update counts for the orders sold to the pool, the // proceeds bought from the pool, and add the LP fees into the reserves // after the swap is performed (LP fees are added in after the swap because // otherwise, they wouldn't be implicitly collected and the reserves wouldn't // grow and compensate the LPs). // // #unchecked // The subtraction of grossFeeT0 and grossFeeT1 from token0In and // token1In, respectively, are unchecked because gross fees are // computed as a fraction less than one (multiplying by a numerator // that is less than the denominator) of the token in values. The resulting // gross fee values will always be less than their respective token in // values. (uint256 token0Out, uint256 token1Out) = _computeVirtualReserves( _evoMem, token0In - grossFeeT0, token1In - grossFeeT1 ); // NOTE: For gas efficiency the order and proceed amounts are summed and then // used to modify the global order and proceed accounting after all // iterations are performed. // // #unchecked // The incrementing of tokenNOrdersU112 below is unchecked because the // order amounts in are checked by Balancer (BAL#526 BALANCE_TOTAL_OVERFLOW) // when orders are taken in by the pool. Repeatedly adding them to a // zero initialized value (tokenNOrdersU112) would take longer to overflow // than this loop could run before it runs out of gas. At the end of the // loop calling this method, these values are then subtracted from the order // accounting and zeroed again for the next execution (the subtraction checks // for underflow on a verified <= U112 value, see BitPackingLib.decrementPairU112). _evoMem.token0OrdersU112 += token0In; _evoMem.token1OrdersU112 += token1In; // The check below for values exceeding U112 cover the unchecked increment of tokenNProceedsU112 // as safe for the duration of this loop. They also cover the call to _incrementScaledProceeds // which depends upon the tokenNOut values being <= U112. (The two one sided active trade use // cases will never produce an output > U112, need to confirm the TWAMM approximation with // two active trades is similar, hence the check below.) requireErrCode(token0Out <= C.MAX_U112, CronErrors.OVERFLOW); requireErrCode(token1Out <= C.MAX_U112, CronErrors.OVERFLOW); _evoMem.token0ProceedsU112 += token0Out; _evoMem.token1ProceedsU112 += token1Out; // Compensate the LPs: // // #unchecked // The incrementing below is unchecked because the lpFeeTN values are a fraction // of the grossFeeTN values which themselves are a fraction of the tokenNIn values // that are known to be limited to 112-bits by the Balancer check when the order is // taken (BAL#526 BALANCE_TOTAL_OVERFLOW). (As discussed above the grossFeeTN values // have a maximum practical value of 116-bits which won't result in a 256-bit overflow // in the increment below before the loop ends). _evoMem.token0ReserveU112 += lpFeeT0; _evoMem.token1ReserveU112 += lpFeeT1; // Distribute proceeds to pools: _incrementScaledProceeds(_loopMem, token0Out, token1Out, currentSalesRate0U112, currentSalesRate1U112); _loopMem.lastVirtualOrderBlock = _expiryBlock; } /// @notice Read only execution of active virtual orders from the last virtual order block /// in state to the current block number. Does not write updates or changes to state. /// @param _evoMem aggregated information for gas efficient virtual order exection. See /// documentation in Structs.sol. /// @param _blockNumber is the block number to execute active virtual orders up to from /// the last virtual order block. /// @dev NOTE: Total FP = 100,000. Thus a fee portion is the number of FP out of 100,000. /// @dev This method's utility comes from the values stored in _evoMem at the end of /// calling it; fees, orders, proceeds and reserves will all reflect the most up-to-date /// values for virtual orders at the current block. /// function _executeVirtualOrdersView(ExecVirtualOrdersMem memory _evoMem, uint256 _blockNumber) private view { uint256 poolFeeLTFP = BitPackingLib.unpackU10(slot1, C.S1_OFFSET_LONG_TERM_FEE_FP); (LoopMem memory loopMem, uint256 expiryBlock) = _getLoopMem(); // Loop through active virtual orders preceeding the final block interval, performing // aggregate Long-Term (LT) swaps and updating sales rates in memory // uint256 prevLVOB = loopMem.lastVirtualOrderBlock; while (expiryBlock < _blockNumber) { _executeVirtualTradesAndOrderExpiries(_evoMem, expiryBlock, loopMem, poolFeeLTFP); _calculateOracleIncrement(_evoMem, prevLVOB, expiryBlock); prevLVOB = loopMem.lastVirtualOrderBlock; // Handle orders expiring at end of interval _decrementSalesRates(expiryBlock, loopMem); expiryBlock += ORDER_BLOCK_INTERVAL; } // Process the active virtual orders of the final block interval, performing // aggregate Long-Term (LT) swaps // if (loopMem.lastVirtualOrderBlock != _blockNumber) { expiryBlock = _blockNumber; _executeVirtualTradesAndOrderExpiries(_evoMem, expiryBlock, loopMem, poolFeeLTFP); _calculateOracleIncrement(_evoMem, prevLVOB, _blockNumber); } } /// @notice Increments the current scaled proceeds of an order pool with the normalized /// proceeds of recent swaps when given the current scaled proceeds, token out amount /// and sales rate of the order pools. /// @param _loopMem aggregated information for gas efficient virtual order loop execution. See /// documentation in Structs.sol. /// @param _token0OutU112 The amount of Token 0 purchased in aggregate in an interval. This amount /// is normalized by the sales rate and then increments the scaled proceeds. /// @param _token1OutU112 The amount of Token 1 purchased in aggregate in an interval. This amount /// is normalized by the sales rate and then increments the scaled proceeds. /// @param _currentSalesRate0U112 The current amount of Token 0 sold per block to the order pool. /// @param _currentSalesRate1U112 The current amount of Token 1 sold per block to the order pool. /// function _incrementScaledProceeds( LoopMem memory _loopMem, uint256 _token0OutU112, uint256 _token1OutU112, uint256 _currentSalesRate0U112, uint256 _currentSalesRate1U112 ) private view { // #overUnderFlowIntended The addition of scaledProceeds below is required to overflow a uint128 // to work properly. This is because when a user withdraws or // cancels their order, the distance between the scaled proceeds // at the time of withdraw/cancel from when their order started is // used to determine their portion. In other words it's not the // value of their scaled proceeds that determines their portion but // the distance between scaled proceeds at withdraw/cancel and order // start. As long as the scaled proceeds doesn't overflow twice // during an order, they have not lost proceeds--overflowing twice // is highly unlikely because of the scaling by the number of decimal // places of the source token in the stored result. Consult Cron-Fi TWAMM // Numerical Analysis for a discussion on this matter. // if (_currentSalesRate0U112 != 0) { _loopMem.scaledProceeds0U128 = uint128( uint128(_loopMem.scaledProceeds0U128) + uint128((_token1OutU112 * ORDER_POOL0_PROCEEDS_SCALING) / _currentSalesRate0U112) ); } if (_currentSalesRate1U112 != 0) { _loopMem.scaledProceeds1U128 = uint128( uint128(_loopMem.scaledProceeds1U128) + uint128((_token0OutU112 * ORDER_POOL1_PROCEEDS_SCALING) / _currentSalesRate1U112) ); } } /// @notice Decrements the sales rates expiring in the specified block, _expiryBlock, from the /// current sales rates. /// @param _expiryBlock is the next block upon which virtual orders expire. It is aligned on /// multiples of the order block interval. /// @param _loopMem aggregated information for gas efficient virtual order loop execution. See /// documentation in Structs.sol. /// function _decrementSalesRates(uint256 _expiryBlock, LoopMem memory _loopMem) private view { // NOTE: Stored sales rates are correct by construction (the packPairU112 method checks for exceeding // MAX_U112), thus these fetched values here are within the range of MAX_U112. (uint256 salesRateEndingPerBlock0U112, uint256 salesRateEndingPerBlock1U112) = BitPackingLib.unpackPairU112( virtualOrders.orderPools.salesRatesEndingPerBlock[_expiryBlock] ); if (salesRateEndingPerBlock0U112 > 0) { requireErrCode(_loopMem.currentSalesRate0U112 >= salesRateEndingPerBlock0U112, CronErrors.UNDERFLOW); _loopMem.currentSalesRate0U112 -= salesRateEndingPerBlock0U112; } if (salesRateEndingPerBlock1U112 > 0) { requireErrCode(_loopMem.currentSalesRate1U112 >= salesRateEndingPerBlock1U112, CronErrors.UNDERFLOW); _loopMem.currentSalesRate1U112 -= salesRateEndingPerBlock1U112; } } /// @notice Converts the provided block number, _updateBlock, to a timestamp and returns the computed /// time elapsed between the timestamp and the previous oracle timestamp. /// If _updateBlock is the current block number, the block.timestamp is used for the timestamp. /// Otherwise the timestamp is computed by subtracting the block time, 12s, multiplied by the /// number of blocks between the current block, block.number, and the provided _updateBlock. /// @param _updateBlock A block number to compute a timestamp for and the elapsed time since the /// previous oracle timestamp. /// NOTE: Expects that all calling functions ensure that updateBlock <= block.number. /// @return timeElapsed The difference between the computed timestamp and the previous oracle timestamp. /// @return blockTimestamp The computed timestamp. /// function _getOracleTimeData(uint256 _updateBlock) private view returns (uint32 timeElapsed, uint32 blockTimestamp) { uint32 oracleTimeStamp = uint32(BitPackingLib.unpackOracleTimeStampS2(slot2)); if (_updateBlock == block.number) { // _updateBlock is the current block number, use the current block timestamp: blockTimestamp = uint32(block.timestamp % 2**32); } else { // _update block is less than the current block, use the current block minus an approximate // amount of time based on the difference multiplied by the block time in seconds guaranteed // by the beacon chain, to generate a timestamp approximation between the current block and // previous last virtual order block: // // #unchecked: // The subtraction of _updateBlock below is unchecked here b/c the calls to // _updateOracle are guaranteed to either pass in a value less than or equal // to the current block number. // // The subtraction of the unelapsed blocks multiplied by the block time from // the timestamp should be able to underflow with the modulus result being // correct for the purposes herein (i.e. the nature of this entire system being // based in differences makes that acceptable). blockTimestamp = uint32((block.timestamp - C.SECONDS_PER_BLOCK * (block.number - _updateBlock)) % 2**32); } // #overUnderFlowIntended // As documented in the Uniswap V2 whitepaper, the 32-bits allocated to // store the timestamp will overflow approximately every 136 years. As // long as users take 1 sample every 136 years, then the resulting underflow // that would occur in the following 32-bit subtraction will be appropriate // as it will yield the correct distance in seconds between the two samples. timeElapsed = blockTimestamp - oracleTimeStamp; } /// @notice Balancer custom pool specific safety checks. /// @param _sender The message sender. Must be the vault address. /// @param _poolId The pool ID in the calling function. Must be this pool. /// function _poolSafetyChecks(address _sender, bytes32 _poolId) private view { requireErrCode(_sender == address(VAULT), CronErrors.NON_VAULT_CALLER); requireErrCode(_poolId == POOL_ID, CronErrors.INCORRECT_POOL_ID); } /// @notice Gets an initialized instance of struct ExecVirtualOrdersMem. The /// orders, proceeds and fees struct members are all initialized to zero. /// The reserves are initialized to the difference between the /// Balancer Vault's notion of the pool's token balances and the sum /// of the orders, proceeds, and fee counters in state. /// State is also used to configure the fee percentages, which are /// explained in more detail below, see NOTE. /// @param _balance0U112 The Balancer Vault balance of Token 0 for this pool. /// Min. = 0, Max. = (2**112) - 1 /// @param _balance1U112 The Balancer Vault balance of Token 1 for this pool. /// Min. = 0, Max. = (2**112) - 1 /// @return evoMem A Struct for executing virtual orders across functions /// efficiently (saves on gas use from continual accesses to /// storage/state). See documentation for ExecVirtualOrdersMem /// struct. /// @dev NOTE: This getter also sets struct members lpFeeU60, feeShareU60 and /// feeShiftU3 based on current state. Explanations for these are /// detailed in the documentation for the ExecVirtualOrdersMem struct. /// For convenience though, note that when Cron-Fi fees are not being /// collected (cronFeeEnabled == C.FALSE), feeShiftU3 and feeShareU60 /// are set to zero as they are not needed since Cron-Fi fees need not /// be calculated. When Cron-Fi fees are collected, then the following /// equations are applicable: /// /// lpFeeU60 = ONE_DU1_18 - balancerFeeDU1F18 (1) /// /// lpFeeU60 /// feeShareU60 = ----------------- (2) /// 1 + 2**feeShiftU3 /// /// The results of equations 1 & 2 above are used in the function /// _updateFees to divide the gross fees between /// Cron-Fi and the Liquidity Providers (LPs), efficiently. The /// sum of those collected fees are then subtracted from the gross fees /// and the remainder goes to Balancer. /// A similar strategy is used when Cron-Fi fees are not collected, /// however there is no need to split fees between Cron-Fi and the LPs /// in that scenario, so feeShiftU3 and feeShareU60 are not needed. /// function _getExecVirtualOrdersMem(uint256 _balance0U112, uint256 _balance1U112) private view returns (ExecVirtualOrdersMem memory evoMem) { (uint256 token0ReserveU112, uint256 token1ReserveU112) = _calculateReserves(_balance0U112, _balance1U112); uint256 localSlot = slot4; // #savegas uint256 cronFeeEnabled = BitPackingLib.unpackBit(localSlot, C.S4_OFFSET_CRON_FEE_ENABLED); uint256 collectBalancerFees = BitPackingLib.unpackBit(localSlot, C.S4_OFFSET_COLLECT_BALANCER_FEES); uint256 balancerFeeDU1F18 = BitPackingLib.unpackBalancerFeeS4(localSlot); // #unchecked // The subtraction below is unchecked because the Balancer Fee, balancerFeeDU1F18, is // retrieved from storage that does not permit values > C.ONE_DU1_18 to be stored. // See predicate in function _handleBalancerFees that ensures potential stored values // are <= C.ONE_DU1_18. uint256 lpFeeU60 = (collectBalancerFees != C.FALSE) ? C.ONE_DU1_18 - balancerFeeDU1F18 : C.ONE_DU1_18; uint256 feeShiftU3; uint256 feeShareU60; if (cronFeeEnabled != C.FALSE) { feeShiftU3 = BitPackingLib.unpackFeeShiftS3(slot3); feeShareU60 = lpFeeU60 / (1 + 2**feeShiftU3); } evoMem = ExecVirtualOrdersMem( token0ReserveU112, token1ReserveU112, lpFeeU60, feeShareU60, feeShiftU3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); } /// @notice Constructs an execute virtual order loop memory struct from virtual orders /// state and the order block interval. /// function _getLoopMem() private view returns (LoopMem memory loopMem, uint256 expiryBlock) { // #unchecked // The subtraction of lvob from the mod of it by the ORDER_BLOCK_INTERVAL // is unchecked because lvob will always be greater than it's modulus by // ORDER_BLOCK_INTERVAL and can't underflow. This is because it is initialized // in the constructor to block.number, which is much greater than the maximum // value of ORDER_BLOCK_INTERVAL (1200, VOLATILE_OBI). // // The addition of ORDER_BLOCK_INTERVAL is unchecked because it is // at most adding 1200 (VOLATILE_OBI) to the previous result, // which is unlikely to overflow unless that result is already near overflow // because block.number is approaching 2**256 - 1. // uint256 lvob = virtualOrders.lastVirtualOrderBlock; // #savegas expiryBlock = lvob - (lvob % ORDER_BLOCK_INTERVAL) + ORDER_BLOCK_INTERVAL; loopMem.lastVirtualOrderBlock = lvob; (loopMem.currentSalesRate0U112, loopMem.currentSalesRate1U112) = BitPackingLib.unpackPairU112( virtualOrders.orderPools.currentSalesRates ); (loopMem.scaledProceeds0U128, loopMem.scaledProceeds1U128) = BitPackingLib.unpackPairU128( virtualOrders.orderPools.scaledProceeds ); } /// @notice Calculates the pool's reserves at the current state or Last Virtual Order Block (LVOB) (i.e. /// not considering unexecuted virtual orders). The calculation is the difference between the /// Balancer Vault notion of the pool's token balances and the sum of the pool's internal /// accounting of orders, proceeds, and fees for both tokens. /// @param _balance0U112 The Balancer Vault balance of Token 0 for this pool. /// Min. = 0, Max. = (2**112) - 1 /// @param _balance1U112 The Balancer Vault balance of Token 1 for this pool. /// Min. = 0, Max. = (2**112) - 1 /// @return token0ReserveU112 The reserves of Token 0 in this pool as of the LVOB. /// Min. = 0, Max. = (2**112) - 1 /// @return token1ReserveU112 The reserves of Token 0 in this pool as of the LVOB. /// Min. = 0, Max. = (2**112) - 1 /// @dev NOTE: _balance0U112 and _balance1U112 can not exceed (2**112) - 1 according to Balancer /// documentation and code comments. This was further confirmed in conversations with /// Balancer's engineering team. /// [https://dev.balancer.fi/deep-dive/guided-tour-of-balancer-vault/episode-2-joins#vault-poolbalances.sol-5] /// That means that if this function does not revert, we are assured that token0ReserveU112 /// and token1ReserveU112 are < MAX_U112. /// @dev #RISK: If the _balance0U112 and/or _balance1U112 values exceed the sum of the respective /// token accounting (orders, proceeds, fees), this function will revert, rendering the /// pool unusable. Even if paused! /// We have mitigated this risk using the onJoinPool function when JoinType.Reward is /// specified. In this scenario, a pool problem will have been identified and the pool /// will have been paused. Anyone could then reward the pool with sufficient liquidity /// to ensure the difference no longer underflows, enabling withdraw, cancel, and exit /// operations. /// function _calculateReserves(uint256 _balance0U112, uint256 _balance1U112) private view returns (uint256 token0ReserveU112, uint256 token1ReserveU112) { (uint256 token0OrdersU112, uint256 token1OrdersU112) = BitPackingLib.unpackPairU112(slot1); (uint256 token0ProceedsU112, uint256 token1ProceedsU112) = BitPackingLib.unpackPairU112(slot2); uint256 localSlot4 = slot4; (uint256 token0BalancerFeesU96, uint256 token1BalancerFeesU96) = BitPackingLib.unpackPairU96(localSlot4); // NOTE: Optimization - prevent un-necessary read of slot3 by checking whether the Cron-Fi Fees stored // in slot3 are zero (in flag zeroCronFiFees stored in slot4): // uint256 token0CronFiFeesU96; uint256 token1CronFiFeesU96; uint256 zeroCronFiFees = BitPackingLib.unpackBit(localSlot4, C.S4_OFFSET_ZERO_CRONFI_FEES); if (zeroCronFiFees == C.FALSE) { (token0CronFiFeesU96, token1CronFiFeesU96) = BitPackingLib.unpackPairU96(slot3); } /// #unchecked /// The orders, proceeds and fees summed below are correct by construction (that is to say, /// their values cannot exceed 112-bits or 96-bits, because they are stored as only 112-bits /// or 96-bits and checked at storage time to ensure these limits aren't exceeded). /// The sum of four 112-bit numbers cannot exceed 116-bits, and thus the unchecked addition /// because a 256-bit overflow is impossible. /// Safe math is used for the subtraction though in case an unforeseen error results in /// the sum of orders, proceeds and fees exceeding Balancer's notion of the the pool's total /// balance of either token. token0ReserveU112 = _balance0U112.sub( token0OrdersU112 + token0ProceedsU112 + token0BalancerFeesU96 + token0CronFiFeesU96 ); token1ReserveU112 = _balance1U112.sub( token1OrdersU112 + token1ProceedsU112 + token1BalancerFeesU96 + token1CronFiFeesU96 ); } /// @notice Computes the virtual reserves and output amounts of the pool for active /// virtual orders. Handles the no-active orders, swap of Token0 to Token1 only /// active orders, Token1 to Token0 only active orders and concurrent swap between /// Token0 and Token1 active orders. /// Updated virtual reserves are returned in the _evoMem struct and output amounts /// through the token0OutU112 and token1OutU112 variables. /// @param _evoMem aggregated information for gas efficient virtual order exection. See /// documentation in Structs.sol. /// @param _token0InU112 amount of Token0 to sell to the pool in exchange for Token1 for active /// virtual orders swapping in this direction. /// @param _token1InU112 amount of Token1 to sell to the pool in exchange for Token0 for active /// virtual orders swapping in this direction. /// @return token0OutU112 amount of Token0 received from swapping _token1InU112 Token1 with the pool. /// @return token1OutU112 amount of Token1 received from swapping _token0InU112 Token0 with the pool. /// function _computeVirtualReserves( ExecVirtualOrdersMem memory _evoMem, uint256 _token0InU112, uint256 _token1InU112 ) private pure returns (uint256 token0OutU112, uint256 token1OutU112) { if (_token0InU112 != 0 || _token1InU112 != 0) { uint256 token0ReserveU112 = _evoMem.token0ReserveU112; // #savegas #savesize uint256 token1ReserveU112 = _evoMem.token1ReserveU112; if (_token0InU112 == 0) { // For single pool selling, use CPAMM formula: // // #unchecked // The increment of token1ReserveU112 is unchecked below because the // _token1InU112 value is derived from the orders which are checked along // with the reserves by Balancer not to exceed U112 (BAL#526 // BALANCE_TOTAL_OVERFLOW). Also, at the start of execute virtual orders // cycles, the token1ReserveU112 is computed from a known U112 value (the // Balancer Vault balance) by subtracting orders, proceeds and fees accounting. // // The multiplication of token0ReserveU112 and _token1InU112 is similarly // constrained to U112 because of the BAL#526 checks performed. The maximum // product of these two values is therefore a U224, which will not overflow // the U256 container (especially given the division by token1ReserveU112). // // The subtraction of token0OutU112 from token0ReserveU112 is unchecked // because token0OutU112 is derived from multiplying token0ReserveU112 by // values that amount to being less than 1, thus reducing it in comparison // to token0ReserveU112. token1ReserveU112 += _token1InU112; token0OutU112 = (token0ReserveU112 * _token1InU112) / token1ReserveU112; token0ReserveU112 = token0ReserveU112 - token0OutU112; } else if (_token1InU112 == 0) { // For single pool selling, use CPAMM formula: // // #unchecked // The following 3 lines are unchecked using the same rationale presented // above for trades in the opposite direction. token0ReserveU112 += _token0InU112; token1OutU112 = (token1ReserveU112 * _token0InU112) / token0ReserveU112; token1ReserveU112 = token1ReserveU112 - token1OutU112; } else { // When both pools sell, apply the TWAMM formula in the form of the FRAX Approximation // // #unchecked // The addition of tokenNReserveU112 to _tokenNInU112 below is unchecked // because both values are known to be <= U112 by Balancer overflow checks // (BAL#526 BALANCE_TOTAL_OVERFLOW). Also as noted in the single-sided rationale // above, the reserves are computed by subtracting order, proceeds and fees // accounting from known U112 values at the start of any execute virtual orders // cycle (the Balancer Vault balance). uint256 sum0 = token0ReserveU112 + _token0InU112; uint256 sum1 = token1ReserveU112 + _token1InU112; // NOTE: purposely using standard rounding (divDown = /) here to reduce operating error. // // #unchecked // Multiplication of token0ReserveU112 and sum1 is unchecked because these // values are known to max out at U112 and U113, the product of which is U225, // much less than the U256 container they're stored in. // // Multiplication by token0ReserveU112 and token1ReserveU112, values known to // be within U112 by Balancer overflow checks and their construction at the start // of the execute virtual orders cycle, cannot exceed a U224 value, thus safe // from overflow. uint256 ammEndToken0 = (token1ReserveU112 * sum0) / sum1; uint256 ammEndToken1 = (token0ReserveU112 * sum1) / sum0; token0ReserveU112 = ammEndToken0; token1ReserveU112 = ammEndToken1; // #unchecked // Both subtractions below are unchecked for underflow because they take the form: // // x * z // z - ------- // y // // where x <= y // // // Full explanation for token0OutU112 & token1OutU112: // // token0OutU112 = sum0 - ammEndToken0 // // sum0 * token1ReserveU112 // = sum0 - -------------------------- // sum1 // // sum0 * token1ReserveU112 // = sum0 - ----------------------------------- // token1ReserveU112 + _token1InU112 // // where: token1ReserveU112 <= token1ReserveU112 + token1InU112 // // // token1OutU112 = sum1 - ammEndToken1 // // sum1 * token0ReserveU112 // token1OutU112 = sum1 - -------------------------- // sum0 // // sum1 * token0ReserveU112 // = sum1 - ------------------------------------ // (token0ReserveU112 + token0InU112) // // where: token0ReserveU112 <= token0ReserveU112 + token0InU112 // token0OutU112 = sum0 - ammEndToken0; token1OutU112 = sum1 - ammEndToken1; } _evoMem.token0ReserveU112 = token0ReserveU112; _evoMem.token1ReserveU112 = token1ReserveU112; } } /// @notice Updates the fees collected for Balancer, Liquidity Providers (LPs) and Cron-Fi (if /// enabled) in the provided struct, _evoMem, for later addition to the pool. /// @param _grossFeeT0 is the gross fee amount collected by the pool for performing LT swaps. /// Min. = 0, Max. = (2**116) - 1 /// @param _grossFeeT1 is the gross fee amount collected by the pool for performing LT swaps. /// Min. = 0, Max. = (2**116) - 1 /// /// @dev see _executeVirtualTradesAndOrderExpiries for analysis explaining /// why _grossFeeT0 and _grossFeeT1 are limited to MAX_U116. /// function _updateFees( ExecVirtualOrdersMem memory _evoMem, uint256 _grossFeeT0, uint256 _grossFeeT1 ) private pure returns (uint256 lpFeeT0, uint256 lpFeeT1) { uint256 feeShiftU3 = _evoMem.feeShiftU3; // #savegas #savesize uint256 lpFeeU60 = _evoMem.lpFeeU60; // #savegas #savesize if (feeShiftU3 == 0) { // Cron-Fi Fees Not Collected: // #unchecked // Multiplication of the gross fees, _grossFeeT0 and _grossFeeT1, // with lp fee, lpFeeU60, is unchecked because the gross fees // max out at 116-bits and the lp fee, lpFeeU60, at 60-bits, the // product of which has a theoretical maximum of 186-bits, far less // than the 256-bit result container. // // NOTE: division with rounding down is performed to protect against underflow // in subtraction below. lpFeeT0 = (_grossFeeT0 * lpFeeU60).divDown(C.DENOMINATOR_DU1_18); lpFeeT1 = (_grossFeeT1 * lpFeeU60).divDown(C.DENOMINATOR_DU1_18); // Accumulate fees for balancer // #unchecked // The accumulation of gross fees for balancer involves unchecked // subtraction because _grossFeeT0 and _grossFeeT1 will always // be greater than lpFeeT0 and lpFeeT1, respectively. This is // because lpFee0 and lpFee1 are derived from the gross fees // by multipling them with a fraction less than one (multiplying // by a numerator that is less than the denominator, C.DENOMINATOR_FP18). // // Overflow is also not checked here in the addition because the // values are clamped to 96-bits when stored in state and the // theoretical maximum _grossFee of 116-bits when added to // 96-bits would not exceed the 256-bit intermediate container resolution. // // Predicate prevents unintended rounding error based balancer fee collection when // balancer fees are zero. (see CronV1Pool.sol::_getExecVirtualOrdersMem documentation). if (lpFeeU60 < C.ONE_DU1_18) { _evoMem.token0BalancerFeesU96 += _grossFeeT0 - lpFeeT0; _evoMem.token1BalancerFeesU96 += _grossFeeT1 - lpFeeT1; } } else { // Cron-Fi Fees Collected: // // NOTE: When the fee address is set, Cron-Fi splits the fees with the LPs // and balancer. The feeShareU60 value is 1/(1+2**feeShift) multiplied by the // fee remaining from collecting the Balancer Protocol fee. This value is then // aportioned with 1 share going to Cron-Fi and (2**feeShift) shares to the LPs. // For example if the Balancer Protocol fee is half of all fees collected // and feeShift=1, then 1/6 of the total collected fee goes to Cron-Fi and // 1/3 of the fees collected go to the LPs (the other 1/2 of collected fees // going to Balancer). uint256 feeShareU60 = _evoMem.feeShareU60; // #savegas #savesize // #unchecked // Multiplication of the gross fees, _grossFeeT0 and _grossFeeT1, // with fee share, feeShareU60, is unchecked because the gross fees // max out at 116-bits and the lp fee, feeShareU60, at 60-bits, the // product of which has a theoretical maximum of 186-bits, far less // than the 256-bit result container. // // NOTE: division with rounding down is performed to protect against underflow // in subtraction below. uint256 feeShareT0 = (_grossFeeT0 * feeShareU60).divDown(C.DENOMINATOR_DU1_18); uint256 feeShareT1 = (_grossFeeT1 * feeShareU60).divDown(C.DENOMINATOR_DU1_18); // LPs get 2**_evoMem.feeShiftU3 of the fee shares, Cron Fi gets one share. // // #unchecked // The left shifts of feeShareT0 and feeShareT1 below are unchecked // because the maximum shift, feeShiftU3, is limited to 4 (see // check in CronV1Pool.sol::setFeeShift. The worst case value of // feeShareT0 and feeShareT1 is 186-bits before division by // C.DENOMINATOR_DU1_18, well within the 256-bit container limit. lpFeeT0 = feeShareT0 << feeShiftU3; lpFeeT1 = feeShareT1 << feeShiftU3; // Accumulate fees for balancer // #unchecked // The accumulation of fees for balancer involves unchecked // subtraction because _grossFeeT0 and _grossFeeT1 will always // be greater than lpFeeT0, lpFeeT1 feeShareT0, and feeShareT1 combined, // respectively. This is because lpFee0, lpFee1, feeShareT0 and feeShareT1 // are derived from the gross fees by multipling them with a fraction less // than or equal to one (multiplying by a numerator that is less than or equal // to the denominator, C.DENOMINATOR_FP18, which rounds down). (The value of // feeShareU60 is such that multiplying it by 2**feeShift and then adding it // to itself will sum to the gross fee minus fees collected for balancer). // // Overflow is also not checked here in the addition because the // values are clamped to 96-bits when stored in state and the // theortical maximum _grossFee of 116-bits when added to // 96-bits would not exceed the 256-bit intermediate container range. // // Predicate prevents unintended rounding error based balancer fee collection when // balancer fees are zero. (see CronV1Pool.sol::_getExecVirtualOrdersMem documentation). if (lpFeeU60 < C.ONE_DU1_18) { _evoMem.token0BalancerFeesU96 += (_grossFeeT0 - lpFeeT0) - feeShareT0; _evoMem.token1BalancerFeesU96 += (_grossFeeT1 - lpFeeT1) - feeShareT1; } // Accumulate fees for CronFi // #unchecked // Similar to above, overflow is not checked in the addition because // Cron-Fi fees are clamped to 96-bits when stored in state and the // theoretical maximum _grossFee of 116-bits (which greatly exceeds // the feeShareT0 and feeShareT1 values) when added to 96-bits would not // exceed the 256-bit intermediate container range. _evoMem.token0CronFiFeesU96 += feeShareT0; _evoMem.token1CronFiFeesU96 += feeShareT1; } } /// @notice Calculate the oracle increment by incrementing the oracle change struct members /// (token0OracleU256F112 and token1OracleU256F112) of struct _evoMem based on the /// current reserve values and the time elapsed since the last reserve values. /// @param _evoMem aggregated information for gas efficient virtual order exection. See /// documentation in Structs.sol. /// @param _lastVirtualOrderBlock is the ethereum block number before the last virtual /// orders were executed. /// @param _expiryBlock is the next block upon which virtual orders expire. It is aligned /// on multiples of the order block interval. function _calculateOracleIncrement( ExecVirtualOrdersMem memory _evoMem, uint256 _lastVirtualOrderBlock, uint256 _expiryBlock ) private pure { // #unchecked // Overflow is not possible in the shift below: U256 > (U112 << 112). // Token reserve values known to be U112 because computed from Balancer Vault values // by subtracting accounting and funds going in/out of them all known to sum to // less than U112 mathematically. // // The subtraction of lastVirtualOrderBlock from _expiryBlock is unchecked below // because the following functions that call this function ensure that _expiryBlock // is greater than _lastVirtualOrderBlock in their call to _getLoopMem, which sets // expiryBlock to the next block number divisible by order block interval after the // last virtual order block: // - executeVirtualOrdersToBlock // - executeVirtualOrdersView // // #overUnderFlowIntended // The UQ256x112 incremented result relies upon and expects overflow // and is unchecked. The reason is that it is the difference between // price oracle samples over time that matters, not the absolute value. // As noted above, see the Uniswap V2 whitepaper. // That said, this is a partial increment that will be added to the // stored accumulation of values and is unlikely to overflow during // virtual order execution because both tokenXOracleU256F112 values // are initialized to zero at the start of execution (the time elapsed // would need to be on the order of 2 ** 32 to overflow the worst case // ratio of token reserves ((2 ** 112-1) / 1)--that means // (2**32-1/12s) = 357913941 blocks must elapse since the last oracle // update and the worst case reserves to overflow this incremental value // and cause an oracle price error. // uint256 timeElapsed = C.SECONDS_PER_BLOCK * (_expiryBlock - _lastVirtualOrderBlock); if (_evoMem.token0ReserveU112 > 0) { _evoMem.token0OracleU256F112 += ((_evoMem.token1ReserveU112 << 112) / _evoMem.token0ReserveU112) * timeElapsed; } if (_evoMem.token1ReserveU112 > 0) { _evoMem.token1OracleU256F112 += ((_evoMem.token0ReserveU112 << 112) / _evoMem.token1ReserveU112) * timeElapsed; } } /// @notice /// @param _token0InU112 is the amount of Token 0 to Join the pool with. /// Min. = 0, Max. = (2**112) - 1 /// @param _token1InU112 is the amount of Token 1 to Join the pool with. /// Min. = 0, Max. = (2**112) - 1 /// @param _token0MinU112 is the minimum price of Token 0 permitted. /// Min. = 0, Max. = (2**112) - 1 /// @param _token1MinU112 is the minimum price of Token 1 permitted. /// Min. = 0, Max. = (2**112) - 1 /// @param _token0ReserveU112 is the current Token 0 reserves of the pool. /// Min. = 0, Max. = (2**112) - 1 /// @param _token1ReserveU112 is the current Token 1 reserves of the pool. /// Min. = 0, Max. = (2**112) - 1 /// function _joinMinimumCheck( uint256 _token0InU112, uint256 _token1InU112, uint256 _token0MinU112, uint256 _token1MinU112, uint256 _token0ReserveU112, uint256 _token1ReserveU112 ) private pure { if (_token0ReserveU112 > 0 && _token1ReserveU112 > 0) { // #unchecked // The multiplication below is unchecked for overflow because the product // of two 112-bit values is less than 256-bits. (Reserves are correct by // construction and Balancer ensures that the tokens in do not cause the // pool to exceed 112-bits). uint256 nominalToken0 = (_token1InU112 * _token0ReserveU112) / _token1ReserveU112; uint256 nominalToken1 = (_token0InU112 * _token1ReserveU112) / _token0ReserveU112; requireErrCode( nominalToken0 >= _token0MinU112 && nominalToken1 >= _token1MinU112, CronErrors.MINIMUM_NOT_SATISFIED ); } } }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.7.0; import "./BalancerErrors.sol"; import "./IAuthentication.sol"; /** * @dev Building block for performing access control on external functions. * * This contract is used via the `authenticate` modifier (or the `_authenticateCaller` function), which can be applied * to external functions to only make them callable by authorized accounts. * * Derived contracts must implement the `_canPerform` function, which holds the actual access control logic. */ abstract contract Authentication is IAuthentication { bytes32 private immutable _actionIdDisambiguator; /** * @dev The main purpose of the `actionIdDisambiguator` is to prevent accidental function selector collisions in * multi contract systems. * * There are two main uses for it: * - if the contract is a singleton, any unique identifier can be used to make the associated action identifiers * unique. The contract's own address is a good option. * - if the contract belongs to a family that shares action identifiers for the same functions, an identifier * shared by the entire family (and no other contract) should be used instead. */ constructor(bytes32 actionIdDisambiguator) { _actionIdDisambiguator = actionIdDisambiguator; } /** * @dev Reverts unless the caller is allowed to call this function. Should only be applied to external functions. */ modifier authenticate() { _authenticateCaller(); _; } /** * @dev Reverts unless the caller is allowed to call the entry point function. */ function _authenticateCaller() internal view { bytes32 actionId = getActionId(msg.sig); _require(_canPerform(actionId, msg.sender), Errors.SENDER_NOT_ALLOWED); } function getActionId(bytes4 selector) public view override returns (bytes32) { // Each external function is dynamically assigned an action identifier as the hash of the disambiguator and the // function selector. Disambiguation is necessary to avoid potential collisions in the function selectors of // multiple contracts. return keccak256(abi.encodePacked(_actionIdDisambiguator, selector)); } function _canPerform(bytes32 actionId, address user) internal view virtual returns (bool); }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.7.0; // solhint-disable /** * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are * supported. */ function _require(bool condition, uint256 errorCode) pure { if (!condition) _revert(errorCode); } /** * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported. */ function _revert(uint256 errorCode) pure { // We're going to dynamically create a revert string based on the error code, with the following format: // 'BAL#{errorCode}' // where the code is left-padded with zeroes to three digits (so they range from 000 to 999). // // We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a // number (8 to 16 bits) than the individual string characters. // // The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a // much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a // safe place to rely on it without worrying about how its usage might affect e.g. memory contents. assembly { // First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999 // range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for // the '0' character. let units := add(mod(errorCode, 10), 0x30) errorCode := div(errorCode, 10) let tenths := add(mod(errorCode, 10), 0x30) errorCode := div(errorCode, 10) let hundreds := add(mod(errorCode, 10), 0x30) // With the individual characters, we can now construct the full string. The "BAL#" part is a known constant // (0x42414c23): we simply shift this by 24 (to provide space for the 3 bytes of the error code), and add the // characters to it, each shifted by a multiple of 8. // The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits // per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte // array). let revertReason := shl(200, add(0x42414c23000000, add(add(units, shl(8, tenths)), shl(16, hundreds)))) // We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded // message will have the following layout: // [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ] // The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We // also write zeroes to the next 28 bytes of memory, but those are about to be overwritten. mstore(0x0, 0x08c379a000000000000000000000000000000000000000000000000000000000) // Next is the offset to the location of the string, which will be placed immediately after (20 bytes away). mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020) // The string length is fixed: 7 characters. mstore(0x24, 7) // Finally, the string itself is stored. mstore(0x44, revertReason) // Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of // the encoded message is therefore 4 + 32 + 32 + 32 = 100. revert(0, 100) } } library Errors { // Math uint256 internal constant ADD_OVERFLOW = 0; uint256 internal constant SUB_OVERFLOW = 1; uint256 internal constant SUB_UNDERFLOW = 2; uint256 internal constant MUL_OVERFLOW = 3; uint256 internal constant ZERO_DIVISION = 4; uint256 internal constant DIV_INTERNAL = 5; uint256 internal constant X_OUT_OF_BOUNDS = 6; uint256 internal constant Y_OUT_OF_BOUNDS = 7; uint256 internal constant PRODUCT_OUT_OF_BOUNDS = 8; uint256 internal constant INVALID_EXPONENT = 9; // Input uint256 internal constant OUT_OF_BOUNDS = 100; uint256 internal constant UNSORTED_ARRAY = 101; uint256 internal constant UNSORTED_TOKENS = 102; uint256 internal constant INPUT_LENGTH_MISMATCH = 103; uint256 internal constant ZERO_TOKEN = 104; // Shared pools uint256 internal constant MIN_TOKENS = 200; uint256 internal constant MAX_TOKENS = 201; uint256 internal constant MAX_SWAP_FEE_PERCENTAGE = 202; uint256 internal constant MIN_SWAP_FEE_PERCENTAGE = 203; uint256 internal constant MINIMUM_BPT = 204; uint256 internal constant CALLER_NOT_VAULT = 205; uint256 internal constant UNINITIALIZED = 206; uint256 internal constant BPT_IN_MAX_AMOUNT = 207; uint256 internal constant BPT_OUT_MIN_AMOUNT = 208; uint256 internal constant EXPIRED_PERMIT = 209; // Pools uint256 internal constant MIN_AMP = 300; uint256 internal constant MAX_AMP = 301; uint256 internal constant MIN_WEIGHT = 302; uint256 internal constant MAX_STABLE_TOKENS = 303; uint256 internal constant MAX_IN_RATIO = 304; uint256 internal constant MAX_OUT_RATIO = 305; uint256 internal constant MIN_BPT_IN_FOR_TOKEN_OUT = 306; uint256 internal constant MAX_OUT_BPT_FOR_TOKEN_IN = 307; uint256 internal constant NORMALIZED_WEIGHT_INVARIANT = 308; uint256 internal constant INVALID_TOKEN = 309; uint256 internal constant UNHANDLED_JOIN_KIND = 310; uint256 internal constant ZERO_INVARIANT = 311; uint256 internal constant ORACLE_INVALID_SECONDS_QUERY = 312; uint256 internal constant ORACLE_NOT_INITIALIZED = 313; uint256 internal constant ORACLE_QUERY_TOO_OLD = 314; uint256 internal constant ORACLE_INVALID_INDEX = 315; uint256 internal constant ORACLE_BAD_SECS = 316; // Lib uint256 internal constant REENTRANCY = 400; uint256 internal constant SENDER_NOT_ALLOWED = 401; uint256 internal constant PAUSED = 402; uint256 internal constant PAUSE_WINDOW_EXPIRED = 403; uint256 internal constant MAX_PAUSE_WINDOW_DURATION = 404; uint256 internal constant MAX_BUFFER_PERIOD_DURATION = 405; uint256 internal constant INSUFFICIENT_BALANCE = 406; uint256 internal constant INSUFFICIENT_ALLOWANCE = 407; uint256 internal constant ERC20_TRANSFER_FROM_ZERO_ADDRESS = 408; uint256 internal constant ERC20_TRANSFER_TO_ZERO_ADDRESS = 409; uint256 internal constant ERC20_MINT_TO_ZERO_ADDRESS = 410; uint256 internal constant ERC20_BURN_FROM_ZERO_ADDRESS = 411; uint256 internal constant ERC20_APPROVE_FROM_ZERO_ADDRESS = 412; uint256 internal constant ERC20_APPROVE_TO_ZERO_ADDRESS = 413; uint256 internal constant ERC20_TRANSFER_EXCEEDS_ALLOWANCE = 414; uint256 internal constant ERC20_DECREASED_ALLOWANCE_BELOW_ZERO = 415; uint256 internal constant ERC20_TRANSFER_EXCEEDS_BALANCE = 416; uint256 internal constant ERC20_BURN_EXCEEDS_ALLOWANCE = 417; uint256 internal constant SAFE_ERC20_CALL_FAILED = 418; uint256 internal constant ADDRESS_INSUFFICIENT_BALANCE = 419; uint256 internal constant ADDRESS_CANNOT_SEND_VALUE = 420; uint256 internal constant SAFE_CAST_VALUE_CANT_FIT_INT256 = 421; uint256 internal constant GRANT_SENDER_NOT_ADMIN = 422; uint256 internal constant REVOKE_SENDER_NOT_ADMIN = 423; uint256 internal constant RENOUNCE_SENDER_NOT_ALLOWED = 424; uint256 internal constant BUFFER_PERIOD_EXPIRED = 425; // Vault uint256 internal constant INVALID_POOL_ID = 500; uint256 internal constant CALLER_NOT_POOL = 501; uint256 internal constant SENDER_NOT_ASSET_MANAGER = 502; uint256 internal constant USER_DOESNT_ALLOW_RELAYER = 503; uint256 internal constant INVALID_SIGNATURE = 504; uint256 internal constant EXIT_BELOW_MIN = 505; uint256 internal constant JOIN_ABOVE_MAX = 506; uint256 internal constant SWAP_LIMIT = 507; uint256 internal constant SWAP_DEADLINE = 508; uint256 internal constant CANNOT_SWAP_SAME_TOKEN = 509; uint256 internal constant UNKNOWN_AMOUNT_IN_FIRST_SWAP = 510; uint256 internal constant MALCONSTRUCTED_MULTIHOP_SWAP = 511; uint256 internal constant INTERNAL_BALANCE_OVERFLOW = 512; uint256 internal constant INSUFFICIENT_INTERNAL_BALANCE = 513; uint256 internal constant INVALID_ETH_INTERNAL_BALANCE = 514; uint256 internal constant INVALID_POST_LOAN_BALANCE = 515; uint256 internal constant INSUFFICIENT_ETH = 516; uint256 internal constant UNALLOCATED_ETH = 517; uint256 internal constant ETH_TRANSFER = 518; uint256 internal constant CANNOT_USE_ETH_SENTINEL = 519; uint256 internal constant TOKENS_MISMATCH = 520; uint256 internal constant TOKEN_NOT_REGISTERED = 521; uint256 internal constant TOKEN_ALREADY_REGISTERED = 522; uint256 internal constant TOKENS_ALREADY_SET = 523; uint256 internal constant TOKENS_LENGTH_MUST_BE_2 = 524; uint256 internal constant NONZERO_TOKEN_BALANCE = 525; uint256 internal constant BALANCE_TOTAL_OVERFLOW = 526; uint256 internal constant POOL_NO_TOKENS = 527; uint256 internal constant INSUFFICIENT_FLASH_LOAN_BALANCE = 528; // Fees uint256 internal constant SWAP_FEE_PERCENTAGE_TOO_HIGH = 600; uint256 internal constant FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH = 601; uint256 internal constant INSUFFICIENT_FLASH_LOAN_FEE_AMOUNT = 602; }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.7.0; interface IAuthentication { /** * @dev Returns the action identifier associated with the external function described by `selector`. */ function getActionId(bytes4 selector) external view returns (bytes32); }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.7.0; /** * @dev Interface for the SignatureValidator helper, used to support meta-transactions. */ interface ISignaturesValidator { /** * @dev Returns the EIP712 domain separator. */ function getDomainSeparator() external view returns (bytes32); /** * @dev Returns the next nonce used by an address to sign messages. */ function getNextNonce(address user) external view returns (uint256); }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.7.0; /** * @dev Interface for the TemporarilyPausable helper. */ interface ITemporarilyPausable { /** * @dev Emitted every time the pause state changes by `_setPaused`. */ event PausedStateChanged(bool paused); /** * @dev Returns the current paused state. */ function getPausedState() external view returns ( bool paused, uint256 pauseWindowEndTime, uint256 bufferPeriodEndTime ); }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.7.0; import "../openzeppelin/IERC20.sol"; import "./BalancerErrors.sol"; import "../../vault/interfaces/IAsset.sol"; library InputHelpers { function ensureInputLengthMatch(uint256 a, uint256 b) internal pure { _require(a == b, Errors.INPUT_LENGTH_MISMATCH); } function ensureInputLengthMatch( uint256 a, uint256 b, uint256 c ) internal pure { _require(a == b && b == c, Errors.INPUT_LENGTH_MISMATCH); } function ensureArrayIsSorted(IAsset[] memory array) internal pure { address[] memory addressArray; // solhint-disable-next-line no-inline-assembly assembly { addressArray := array } ensureArrayIsSorted(addressArray); } function ensureArrayIsSorted(IERC20[] memory array) internal pure { address[] memory addressArray; // solhint-disable-next-line no-inline-assembly assembly { addressArray := array } ensureArrayIsSorted(addressArray); } function ensureArrayIsSorted(address[] memory array) internal pure { if (array.length < 2) { return; } address previous = array[0]; for (uint256 i = 1; i < array.length; ++i) { address current = array[i]; _require(previous < current, Errors.UNSORTED_ARRAY); previous = current; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; import "../helpers/BalancerErrors.sol"; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow checks. * Adapted from OpenZeppelin's SafeMath library */ library Math { /** * @dev Returns the addition of two unsigned integers of 256 bits, reverting on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; _require(c >= a, Errors.ADD_OVERFLOW); return c; } /** * @dev Returns the addition of two signed integers, reverting on overflow. */ function add(int256 a, int256 b) internal pure returns (int256) { int256 c = a + b; _require((b >= 0 && c >= a) || (b < 0 && c < a), Errors.ADD_OVERFLOW); return c; } /** * @dev Returns the subtraction of two unsigned integers of 256 bits, reverting on overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { _require(b <= a, Errors.SUB_OVERFLOW); uint256 c = a - b; return c; } /** * @dev Returns the subtraction of two signed integers, reverting on overflow. */ function sub(int256 a, int256 b) internal pure returns (int256) { int256 c = a - b; _require((b >= 0 && c <= a) || (b < 0 && c > a), Errors.SUB_OVERFLOW); return c; } /** * @dev Returns the largest of two numbers of 256 bits. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers of 256 bits. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a * b; _require(a == 0 || c / a == b, Errors.MUL_OVERFLOW); return c; } function divDown(uint256 a, uint256 b) internal pure returns (uint256) { _require(b != 0, Errors.ZERO_DIVISION); return a / b; } function divUp(uint256 a, uint256 b) internal pure returns (uint256) { _require(b != 0, Errors.ZERO_DIVISION); if (a == 0) { return 0; } else { return 1 + (a - 1) / b; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. * * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding * they need in their contracts using a combination of `abi.encode` and `keccak256`. * * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA * ({_hashTypedDataV4}). * * The implementation of the domain separator was designed to be as efficient as possible while still properly updating * the chain id to protect against replay attacks on an eventual fork of the chain. * * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. * * _Available since v3.4._ */ abstract contract EIP712 { /* solhint-disable var-name-mixedcase */ bytes32 private immutable _HASHED_NAME; bytes32 private immutable _HASHED_VERSION; bytes32 private immutable _TYPE_HASH; /* solhint-enable var-name-mixedcase */ /** * @dev Initializes the domain separator and parameter caches. * * The meaning of `name` and `version` is specified in * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: * * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. * - `version`: the current major version of the signing domain. * * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart * contract upgrade]. */ constructor(string memory name, string memory version) { _HASHED_NAME = keccak256(bytes(name)); _HASHED_VERSION = keccak256(bytes(version)); _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); } /** * @dev Returns the domain separator for the current chain. */ function _domainSeparatorV4() internal view virtual returns (bytes32) { return keccak256(abi.encode(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION, _getChainId(), address(this))); } /** * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this * function returns the hash of the fully encoded EIP712 message for this domain. * * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: * * ```solidity * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( * keccak256("Mail(address to,string contents)"), * mailTo, * keccak256(bytes(mailContents)) * ))); * address signer = ECDSA.recover(digest, signature); * ``` */ function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), structHash)); } function _getChainId() private view returns (uint256 chainId) { // Silence state mutability warning without generating bytecode. // See https://github.com/ethereum/solidity/issues/10090#issuecomment-741789128 and // https://github.com/ethereum/solidity/issues/2691 this; // solhint-disable-next-line no-inline-assembly assembly { chainId := chainid() } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.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.7.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over `owner`'s tokens, * given `owner`'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for `permit`, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // Based on the ReentrancyGuard library from OpenZeppelin Contracts, altered to reduce bytecode size. // Modifier code is inlined by the compiler, which causes its code to appear multiple times in the codebase. By using // private functions, we achieve the same end result with slightly higher runtime gas costs, but reduced bytecode size. pragma solidity ^0.7.0; import "../helpers/BalancerErrors.sol"; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { _enterNonReentrant(); _; _exitNonReentrant(); } function _enterNonReentrant() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED _require(_status != _ENTERED, Errors.REENTRANCY); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _exitNonReentrant() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
// SPDX-License-Identifier: MIT // Based on the ReentrancyGuard library from OpenZeppelin Contracts, altered to reduce gas costs. // The `safeTransfer` and `safeTransferFrom` functions assume that `token` is a contract (an account with code), and // work differently from the OpenZeppelin version if it is not. pragma solidity ^0.7.0; import "../helpers/BalancerErrors.sol"; import "./IERC20.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 { function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(address(token), abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(address(token), abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @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). * * WARNING: `token` is assumed to be a contract: calls to EOAs will *not* revert. */ function _callOptionalReturn(address 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. (bool success, bytes memory returndata) = token.call(data); // If the low-level call didn't succeed we return whatever was returned from it. assembly { if eq(success, 0) { returndatacopy(0, 0, returndatasize()) revert(0, returndatasize()) } } // Finally we check the returndata size is either zero or true - note that this check will always pass for EOAs _require(returndata.length == 0 || abi.decode(returndata, (bool)), Errors.SAFE_ERC20_CALL_FAILED); } }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.7.0; import "../lib/math/Math.sol"; import "../lib/openzeppelin/IERC20.sol"; import "../lib/openzeppelin/IERC20Permit.sol"; import "../lib/openzeppelin/EIP712.sol"; /** * @title Highly opinionated token implementation * @author Balancer Labs * @dev * - Includes functions to increase and decrease allowance as a workaround * for the well-known issue with `approve`: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * - Allows for 'infinite allowance', where an allowance of 0xff..ff is not * decreased by calls to transferFrom * - Lets a token holder use `transferFrom` to send their own tokens, * without first setting allowance * - Emits 'Approval' events whenever allowance is changed by `transferFrom` */ contract BalancerPoolToken is IERC20, IERC20Permit, EIP712 { using Math for uint256; // State variables uint8 private constant _DECIMALS = 18; mapping(address => uint256) private _balance; mapping(address => mapping(address => uint256)) private _allowance; uint256 private _totalSupply; string private _name; string private _symbol; mapping(address => uint256) private _nonces; // solhint-disable-next-line var-name-mixedcase bytes32 private immutable _PERMIT_TYPE_HASH = keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ); // Function declarations constructor(string memory tokenName, string memory tokenSymbol) EIP712(tokenName, "1") { _name = tokenName; _symbol = tokenSymbol; } // External functions function allowance(address owner, address spender) external view override returns (uint256) { return _allowance[owner][spender]; } function balanceOf(address account) external view override returns (uint256) { return _balance[account]; } function approve(address spender, uint256 amount) external override returns (bool) { _setAllowance(msg.sender, spender, amount); return true; } function increaseApproval(address spender, uint256 amount) external returns (bool) { _setAllowance(msg.sender, spender, _allowance[msg.sender][spender].add(amount)); return true; } function decreaseApproval(address spender, uint256 amount) external returns (bool) { uint256 currentAllowance = _allowance[msg.sender][spender]; if (amount >= currentAllowance) { _setAllowance(msg.sender, spender, 0); } else { _setAllowance(msg.sender, spender, currentAllowance.sub(amount)); } return true; } function transfer(address recipient, uint256 amount) external override returns (bool) { _move(msg.sender, recipient, amount); return true; } function transferFrom( address sender, address recipient, uint256 amount ) external override returns (bool) { uint256 currentAllowance = _allowance[sender][msg.sender]; _require(msg.sender == sender || currentAllowance >= amount, Errors.INSUFFICIENT_ALLOWANCE); _move(sender, recipient, amount); if (msg.sender != sender && currentAllowance != uint256(-1)) { // Because of the previous require, we know that if msg.sender != sender then currentAllowance >= amount _setAllowance(sender, msg.sender, currentAllowance - amount); } return true; } function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual override { // solhint-disable-next-line not-rely-on-time _require(block.timestamp <= deadline, Errors.EXPIRED_PERMIT); uint256 nonce = _nonces[owner]; bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPE_HASH, owner, spender, value, nonce, deadline)); bytes32 hash = _hashTypedDataV4(structHash); address signer = ecrecover(hash, v, r, s); _require((signer != address(0)) && (signer == owner), Errors.INVALID_SIGNATURE); _nonces[owner] = nonce + 1; _setAllowance(owner, spender, value); } // Public functions function name() public view returns (string memory) { return _name; } function symbol() public view returns (string memory) { return _symbol; } function decimals() public pure returns (uint8) { return _DECIMALS; } function totalSupply() public view override returns (uint256) { return _totalSupply; } function nonces(address owner) external view override returns (uint256) { return _nonces[owner]; } // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view override returns (bytes32) { return _domainSeparatorV4(); } // Internal functions function _mintPoolTokens(address recipient, uint256 amount) internal { _balance[recipient] = _balance[recipient].add(amount); _totalSupply = _totalSupply.add(amount); emit Transfer(address(0), recipient, amount); } function _burnPoolTokens(address sender, uint256 amount) internal { uint256 currentBalance = _balance[sender]; _require(currentBalance >= amount, Errors.INSUFFICIENT_BALANCE); _balance[sender] = currentBalance - amount; _totalSupply = _totalSupply.sub(amount); emit Transfer(sender, address(0), amount); } function _move( address sender, address recipient, uint256 amount ) internal { uint256 currentBalance = _balance[sender]; _require(currentBalance >= amount, Errors.INSUFFICIENT_BALANCE); // Prohibit transfers to the zero address to avoid confusion with the // Transfer event emitted by `_burnPoolTokens` _require(recipient != address(0), Errors.ERC20_TRANSFER_TO_ZERO_ADDRESS); _balance[sender] = currentBalance - amount; _balance[recipient] = _balance[recipient].add(amount); emit Transfer(sender, recipient, amount); } // Private functions function _setAllowance( address owner, address spender, uint256 amount ) private { _allowance[owner][spender] = amount; emit Approval(owner, spender, amount); } }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "../../vault/interfaces/IVault.sol"; import "../../vault/interfaces/IBasePool.sol"; /** * @dev Base contract for Pool factories. * * Pools are deployed from factories to allow third parties to reason about them. Unknown Pools may have arbitrary * logic: being able to assert that a Pool's behavior follows certain rules (those imposed by the contracts created by * the factory) is very powerful. */ abstract contract BasePoolFactory { IVault private immutable _vault; mapping(address => bool) private _isPoolFromFactory; event PoolCreated(address indexed pool); constructor(IVault vault) { _vault = vault; } /** * @dev Returns the Vault's address. */ function getVault() public view returns (IVault) { return _vault; } /** * @dev Returns true if `pool` was created by this factory. */ function isPoolFromFactory(address pool) external view returns (bool) { return _isPoolFromFactory[pool]; } /** * @dev Registers a new created pool. * * Emits a `PoolCreated` event. */ function _register(address pool) internal { _isPoolFromFactory[pool] = true; emit PoolCreated(pool); } }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "../lib/openzeppelin/IERC20.sol"; import "../lib/helpers/InputHelpers.sol"; import "../lib/helpers/Authentication.sol"; import "../lib/openzeppelin/ReentrancyGuard.sol"; import "../lib/openzeppelin/SafeERC20.sol"; import "./interfaces/IVault.sol"; import "./interfaces/IAuthorizer.sol"; /** * @dev This an auxiliary contract to the Vault, deployed by it during construction. It offloads some of the tasks the * Vault performs to reduce its overall bytecode size. * * The current values for all protocol fee percentages are stored here, and any tokens charged as protocol fees are * sent to this contract, where they may be withdrawn by authorized entities. All authorization tasks are delegated * to the Vault's own authorizer. */ contract ProtocolFeesCollector is Authentication, ReentrancyGuard { using SafeERC20 for IERC20; // Absolute maximum fee percentages (1e18 = 100%, 1e16 = 1%). uint256 private constant _MAX_PROTOCOL_SWAP_FEE_PERCENTAGE = 50e16; // 50% uint256 private constant _MAX_PROTOCOL_FLASH_LOAN_FEE_PERCENTAGE = 1e16; // 1% IVault public immutable vault; // All fee percentages are 18-decimal fixed point numbers. // The swap fee is charged whenever a swap occurs, as a percentage of the fee charged by the Pool. These are not // actually charged on each individual swap: the `Vault` relies on the Pools being honest and reporting fees due // when users join and exit them. uint256 private _swapFeePercentage; // The flash loan fee is charged whenever a flash loan occurs, as a percentage of the tokens lent. uint256 private _flashLoanFeePercentage; event SwapFeePercentageChanged(uint256 newSwapFeePercentage); event FlashLoanFeePercentageChanged(uint256 newFlashLoanFeePercentage); constructor(IVault _vault) // The ProtocolFeesCollector is a singleton, so it simply uses its own address to disambiguate action // identifiers. Authentication(bytes32(uint256(address(this)))) { vault = _vault; } function withdrawCollectedFees( IERC20[] calldata tokens, uint256[] calldata amounts, address recipient ) external nonReentrant authenticate { InputHelpers.ensureInputLengthMatch(tokens.length, amounts.length); for (uint256 i = 0; i < tokens.length; ++i) { IERC20 token = tokens[i]; uint256 amount = amounts[i]; token.safeTransfer(recipient, amount); } } // authenticate disabled for testing close to production // function setSwapFeePercentage(uint256 newSwapFeePercentage) external authenticate { function setSwapFeePercentage(uint256 newSwapFeePercentage) external { _require(newSwapFeePercentage <= _MAX_PROTOCOL_SWAP_FEE_PERCENTAGE, Errors.SWAP_FEE_PERCENTAGE_TOO_HIGH); _swapFeePercentage = newSwapFeePercentage; emit SwapFeePercentageChanged(newSwapFeePercentage); } function setFlashLoanFeePercentage(uint256 newFlashLoanFeePercentage) external authenticate { _require( newFlashLoanFeePercentage <= _MAX_PROTOCOL_FLASH_LOAN_FEE_PERCENTAGE, Errors.FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH ); _flashLoanFeePercentage = newFlashLoanFeePercentage; emit FlashLoanFeePercentageChanged(newFlashLoanFeePercentage); } function getSwapFeePercentage() external view returns (uint256) { return _swapFeePercentage; } function getFlashLoanFeePercentage() external view returns (uint256) { return _flashLoanFeePercentage; } function getCollectedFeeAmounts(IERC20[] memory tokens) external view returns (uint256[] memory feeAmounts) { feeAmounts = new uint256[](tokens.length); for (uint256 i = 0; i < tokens.length; ++i) { feeAmounts[i] = tokens[i].balanceOf(address(this)); } } function getAuthorizer() external view returns (IAuthorizer) { return _getAuthorizer(); } function _canPerform(bytes32 actionId, address account) internal view override returns (bool) { return _getAuthorizer().canPerform(actionId, account, address(this)); } function _getAuthorizer() internal view returns (IAuthorizer) { return vault.getAuthorizer(); } }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.7.0; /** * @dev This is an empty interface used to represent either ERC20-conforming token contracts or ETH (using the zero * address sentinel value). We're just relying on the fact that `interface` can be used to declare new address-like * types. * * This concept is unrelated to a Pool's Asset Managers. */ interface IAsset { // solhint-disable-previous-line no-empty-blocks }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.7.0; interface IAuthorizer { /** * @dev Returns true if `account` can perform the action described by `actionId` in the contract `where`. */ function canPerform( bytes32 actionId, address account, address where ) external view returns (bool); }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "./IVault.sol"; import "./IPoolSwapStructs.sol"; /** * @dev Interface for adding and removing liquidity that all Pool contracts should implement. Note that this is not * the complete Pool contract interface, as it is missing the swap hooks. Pool contracts should also inherit from * either IGeneralPool or IMinimalSwapInfoPool */ interface IBasePool is IPoolSwapStructs { /** * @dev Called by the Vault when a user calls `IVault.joinPool` to add liquidity to this Pool. Returns how many of * each registered token the user should provide, as well as the amount of protocol fees the Pool owes to the Vault. * The Vault will then take tokens from `sender` and add them to the Pool's balances, as well as collect * the reported amount in protocol fees, which the pool should calculate based on `protocolSwapFeePercentage`. * * Protocol fees are reported and charged on join events so that the Pool is free of debt whenever new users join. * * `sender` is the account performing the join (from which tokens will be withdrawn), and `recipient` is the account * designated to receive any benefits (typically pool shares). `currentBalances` contains the total balances * for each token the Pool registered in the Vault, in the same order that `IVault.getPoolTokens` would return. * * `lastChangeBlock` is the last block in which *any* of the Pool's registered tokens last changed its total * balance. * * `userData` contains any pool-specific instructions needed to perform the calculations, such as the type of * join (e.g., proportional given an amount of pool shares, single-asset, multi-asset, etc.) * * Contracts implementing this function should check that the caller is indeed the Vault before performing any * state-changing operations, such as minting pool shares. */ function onJoinPool( bytes32 poolId, address sender, address recipient, uint256[] memory balances, uint256 lastChangeBlock, uint256 protocolSwapFeePercentage, bytes memory userData ) external returns (uint256[] memory amountsIn, uint256[] memory dueProtocolFeeAmounts); /** * @dev Called by the Vault when a user calls `IVault.exitPool` to remove liquidity from this Pool. Returns how many * tokens the Vault should deduct from the Pool's balances, as well as the amount of protocol fees the Pool owes * to the Vault. The Vault will then take tokens from the Pool's balances and send them to `recipient`, * as well as collect the reported amount in protocol fees, which the Pool should calculate based on * `protocolSwapFeePercentage`. * * Protocol fees are charged on exit events to guarantee that users exiting the Pool have paid their share. * * `sender` is the account performing the exit (typically the pool shareholder), and `recipient` is the account * to which the Vault will send the proceeds. `currentBalances` contains the total token balances for each token * the Pool registered in the Vault, in the same order that `IVault.getPoolTokens` would return. * * `lastChangeBlock` is the last block in which *any* of the Pool's registered tokens last changed its total * balance. * * `userData` contains any pool-specific instructions needed to perform the calculations, such as the type of * exit (e.g., proportional given an amount of pool shares, single-asset, multi-asset, etc.) * * Contracts implementing this function should check that the caller is indeed the Vault before performing any * state-changing operations, such as burning pool shares. */ function onExitPool( bytes32 poolId, address sender, address recipient, uint256[] memory balances, uint256 lastChangeBlock, uint256 protocolSwapFeePercentage, bytes memory userData ) external returns (uint256[] memory amountsOut, uint256[] memory dueProtocolFeeAmounts); }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.7.0; // Inspired by Aave Protocol's IFlashLoanReceiver. import "../../lib/openzeppelin/IERC20.sol"; interface IFlashLoanRecipient { /** * @dev When `flashLoan` is called on the Vault, it invokes the `receiveFlashLoan` hook on the recipient. * * At the time of the call, the Vault will have transferred `amounts` for `tokens` to the recipient. Before this * call returns, the recipient must have transferred `amounts` plus `feeAmounts` for each token back to the * Vault, or else the entire flash loan will revert. * * `userData` is the same value passed in the `IVault.flashLoan` call. */ function receiveFlashLoan( IERC20[] memory tokens, uint256[] memory amounts, uint256[] memory feeAmounts, bytes memory userData ) external; }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "./IBasePool.sol"; /** * @dev Pool contracts with the MinimalSwapInfo or TwoToken specialization settings should implement this interface. * * This is called by the Vault when a user calls `IVault.swap` or `IVault.batchSwap` to swap with this Pool. * Returns the number of tokens the Pool will grant to the user in a 'given in' swap, or that the user will grant * to the pool in a 'given out' swap. * * This can often be implemented by a `view` function, since many pricing algorithms don't need to track state * changes in swaps. However, contracts implementing this in non-view functions should check that the caller is * indeed the Vault. */ interface IMinimalSwapInfoPool is IBasePool { function onSwap( SwapRequest memory swapRequest, uint256 currentBalanceTokenIn, uint256 currentBalanceTokenOut ) external returns (uint256 amount); }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "../../lib/openzeppelin/IERC20.sol"; import "./IVault.sol"; interface IPoolSwapStructs { // This is not really an interface - it just defines common structs used by other interfaces: IGeneralPool and // IMinimalSwapInfoPool. // // This data structure represents a request for a token swap, where `kind` indicates the swap type ('given in' or // 'given out') which indicates whether or not the amount sent by the pool is known. // // The pool receives `tokenIn` and sends `tokenOut`. `amount` is the number of `tokenIn` tokens the pool will take // in, or the number of `tokenOut` tokens the Pool will send out, depending on the given swap `kind`. // // All other fields are not strictly necessary for most swaps, but are provided to support advanced scenarios in // some Pools. // // `poolId` is the ID of the Pool involved in the swap - this is useful for Pool contracts that implement more than // one Pool. // // The meaning of `lastChangeBlock` depends on the Pool specialization: // - Two Token or Minimal Swap Info: the last block in which either `tokenIn` or `tokenOut` changed its total // balance. // - General: the last block in which *any* of the Pool's registered tokens changed its total balance. // // `from` is the origin address for the funds the Pool receives, and `to` is the destination address // where the Pool sends the outgoing tokens. // // `userData` is extra data provided by the caller - typically a signature from a trusted party. struct SwapRequest { IVault.SwapKind kind; IERC20 tokenIn; IERC20 tokenOut; uint256 amount; // Misc data bytes32 poolId; uint256 lastChangeBlock; address from; address to; bytes userData; } }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma experimental ABIEncoderV2; import "../../lib/openzeppelin/IERC20.sol"; import "./IWETH.sol"; import "./IAsset.sol"; import "./IAuthorizer.sol"; import "./IFlashLoanRecipient.sol"; import "../ProtocolFeesCollector.sol"; import "../../lib/helpers/ISignaturesValidator.sol"; import "../../lib/helpers/ITemporarilyPausable.sol"; pragma solidity ^0.7.0; /** * @dev Full external interface for the Vault core contract - no external or public methods exist in the contract that * don't override one of these declarations. */ interface IVault is ISignaturesValidator, ITemporarilyPausable { // Generalities about the Vault: // // - Whenever documentation refers to 'tokens', it strictly refers to ERC20-compliant token contracts. Tokens are // transferred out of the Vault by calling the `IERC20.transfer` function, and transferred in by calling // `IERC20.transferFrom`. In these cases, the sender must have previously allowed the Vault to use their tokens by // calling `IERC20.approve`. The only deviation from the ERC20 standard that is supported is functions not returning // a boolean value: in these scenarios, a non-reverting call is assumed to be successful. // // - All non-view functions in the Vault are non-reentrant: calling them while another one is mid-execution (e.g. // while execution control is transferred to a token contract during a swap) will result in a revert. View // functions can be called in a re-reentrant way, but doing so might cause them to return inconsistent results. // Contracts calling view functions in the Vault must make sure the Vault has not already been entered. // // - View functions revert if referring to either unregistered Pools, or unregistered tokens for registered Pools. // Authorizer // // Some system actions are permissioned, like setting and collecting protocol fees. This permissioning system exists // outside of the Vault in the Authorizer contract: the Vault simply calls the Authorizer to check if the caller // can perform a given action. /** * @dev Returns the Vault's Authorizer. */ function getAuthorizer() external view returns (IAuthorizer); /** * @dev Sets a new Authorizer for the Vault. The caller must be allowed by the current Authorizer to do this. * * Emits an `AuthorizerChanged` event. */ function setAuthorizer(IAuthorizer newAuthorizer) external; /** * @dev Emitted when a new authorizer is set by `setAuthorizer`. */ event AuthorizerChanged(IAuthorizer indexed newAuthorizer); // Relayers // // Additionally, it is possible for an account to perform certain actions on behalf of another one, using their // Vault ERC20 allowance and Internal Balance. These accounts are said to be 'relayers' for these Vault functions, // and are expected to be smart contracts with sound authentication mechanisms. For an account to be able to wield // this power, two things must occur: // - The Authorizer must grant the account the permission to be a relayer for the relevant Vault function. This // means that Balancer governance must approve each individual contract to act as a relayer for the intended // functions. // - Each user must approve the relayer to act on their behalf. // This double protection means users cannot be tricked into approving malicious relayers (because they will not // have been allowed by the Authorizer via governance), nor can malicious relayers approved by a compromised // Authorizer or governance drain user funds, since they would also need to be approved by each individual user. /** * @dev Returns true if `user` has approved `relayer` to act as a relayer for them. */ function hasApprovedRelayer(address user, address relayer) external view returns (bool); /** * @dev Allows `relayer` to act as a relayer for `sender` if `approved` is true, and disallows it otherwise. * * Emits a `RelayerApprovalChanged` event. */ function setRelayerApproval( address sender, address relayer, bool approved ) external; /** * @dev Emitted every time a relayer is approved or disapproved by `setRelayerApproval`. */ event RelayerApprovalChanged(address indexed relayer, address indexed sender, bool approved); // Internal Balance // // Users can deposit tokens into the Vault, where they are allocated to their Internal Balance, and later // transferred or withdrawn. It can also be used as a source of tokens when joining Pools, as a destination // when exiting them, and as either when performing swaps. This usage of Internal Balance results in greatly reduced // gas costs when compared to relying on plain ERC20 transfers, leading to large savings for frequent users. // // Internal Balance management features batching, which means a single contract call can be used to perform multiple // operations of different kinds, with different senders and recipients, at once. /** * @dev Returns `user`'s Internal Balance for a set of tokens. */ function getInternalBalance(address user, IERC20[] memory tokens) external view returns (uint256[] memory); /** * @dev Performs a set of user balance operations, which involve Internal Balance (deposit, withdraw or transfer) * and plain ERC20 transfers using the Vault's allowance. This last feature is particularly useful for relayers, as * it lets integrators reuse a user's Vault allowance. * * For each operation, if the caller is not `sender`, it must be an authorized relayer for them. */ function manageUserBalance(UserBalanceOp[] memory ops) external payable; /** * @dev Data for `manageUserBalance` operations, which include the possibility for ETH to be sent and received without manual WETH wrapping or unwrapping. */ struct UserBalanceOp { UserBalanceOpKind kind; IAsset asset; uint256 amount; address sender; address payable recipient; } // There are four possible operations in `manageUserBalance`: // // - DEPOSIT_INTERNAL // Increases the Internal Balance of the `recipient` account by transferring tokens from the corresponding // `sender`. The sender must have allowed the Vault to use their tokens via `IERC20.approve()`. // // ETH can be used by passing the ETH sentinel value as the asset and forwarding ETH in the call: it will be wrapped // and deposited as WETH. Any ETH amount remaining will be sent back to the caller (not the sender, which is // relevant for relayers). // // Emits an `InternalBalanceChanged` event. // // // - WITHDRAW_INTERNAL // Decreases the Internal Balance of the `sender` account by transferring tokens to the `recipient`. // // ETH can be used by passing the ETH sentinel value as the asset. This will deduct WETH instead, unwrap it and send // it to the recipient as ETH. // // Emits an `InternalBalanceChanged` event. // // // - TRANSFER_INTERNAL // Transfers tokens from the Internal Balance of the `sender` account to the Internal Balance of `recipient`. // // Reverts if the ETH sentinel value is passed. // // Emits an `InternalBalanceChanged` event. // // // - TRANSFER_EXTERNAL // Transfers tokens from `sender` to `recipient`, using the Vault's ERC20 allowance. This is typically used by // relayers, as it lets them reuse a user's Vault allowance. // // Reverts if the ETH sentinel value is passed. // // Emits an `ExternalBalanceTransfer` event. enum UserBalanceOpKind { DEPOSIT_INTERNAL, WITHDRAW_INTERNAL, TRANSFER_INTERNAL, TRANSFER_EXTERNAL } /** * @dev Emitted when a user's Internal Balance changes, either from calls to `manageUserBalance`, or through * interacting with Pools using Internal Balance. * * Because Internal Balance works exclusively with ERC20 tokens, ETH deposits and withdrawals will use the WETH * address. */ event InternalBalanceChanged(address indexed user, IERC20 indexed token, int256 delta); /** * @dev Emitted when a user's Vault ERC20 allowance is used by the Vault to transfer tokens to an external account. */ event ExternalBalanceTransfer(IERC20 indexed token, address indexed sender, address recipient, uint256 amount); // Pools // // There are three specialization settings for Pools, which allow for cheaper swaps at the cost of reduced // functionality: // // - General: no specialization, suited for all Pools. IGeneralPool is used for swap request callbacks, passing the // balance of all tokens in the Pool. These Pools have the largest swap costs (because of the extra storage reads), // which increase with the number of registered tokens. // // - Minimal Swap Info: IMinimalSwapInfoPool is used instead of IGeneralPool, which saves gas by only passing the // balance of the two tokens involved in the swap. This is suitable for some pricing algorithms, like the weighted // constant product one popularized by Balancer V1. Swap costs are smaller compared to general Pools, and are // independent of the number of registered tokens. // // - Two Token: only allows two tokens to be registered. This achieves the lowest possible swap gas cost. Like // minimal swap info Pools, these are called via IMinimalSwapInfoPool. enum PoolSpecialization { GENERAL, MINIMAL_SWAP_INFO, TWO_TOKEN } /** * @dev Registers the caller account as a Pool with a given specialization setting. Returns the Pool's ID, which * is used in all Pool-related functions. Pools cannot be deregistered, nor can the Pool's specialization be * changed. * * The caller is expected to be a smart contract that implements either `IGeneralPool` or `IMinimalSwapInfoPool`, * depending on the chosen specialization setting. This contract is known as the Pool's contract. * * Note that the same contract may register itself as multiple Pools with unique Pool IDs, or in other words, * multiple Pools may share the same contract. * * Emits a `PoolRegistered` event. */ function registerPool(PoolSpecialization specialization) external returns (bytes32); /** * @dev Emitted when a Pool is registered by calling `registerPool`. */ event PoolRegistered(bytes32 indexed poolId, address indexed poolAddress, PoolSpecialization specialization); /** * @dev Returns a Pool's contract address and specialization setting. */ function getPool(bytes32 poolId) external view returns (address, PoolSpecialization); /** * @dev Registers `tokens` for the `poolId` Pool. Must be called by the Pool's contract. * * Pools can only interact with tokens they have registered. Users join a Pool by transferring registered tokens, * exit by receiving registered tokens, and can only swap registered tokens. * * Each token can only be registered once. For Pools with the Two Token specialization, `tokens` must have a length * of two, that is, both tokens must be registered in the same `registerTokens` call, and they must be sorted in * ascending order. * * The `tokens` and `assetManagers` arrays must have the same length, and each entry in these indicates the Asset * Manager for the corresponding token. Asset Managers can manage a Pool's tokens via `managePoolBalance`, * depositing and withdrawing them directly, and can even set their balance to arbitrary amounts. They are therefore * expected to be highly secured smart contracts with sound design principles, and the decision to register an * Asset Manager should not be made lightly. * * Pools can choose not to assign an Asset Manager to a given token by passing in the zero address. Once an Asset * Manager is set, it cannot be changed except by deregistering the associated token and registering again with a * different Asset Manager. * * Emits a `TokensRegistered` event. */ function registerTokens( bytes32 poolId, IERC20[] memory tokens, address[] memory assetManagers ) external; /** * @dev Emitted when a Pool registers tokens by calling `registerTokens`. */ event TokensRegistered(bytes32 indexed poolId, IERC20[] tokens, address[] assetManagers); /** * @dev Deregisters `tokens` for the `poolId` Pool. Must be called by the Pool's contract. * * Only registered tokens (via `registerTokens`) can be deregistered. Additionally, they must have zero total * balance. For Pools with the Two Token specialization, `tokens` must have a length of two, that is, both tokens * must be deregistered in the same `deregisterTokens` call. * * A deregistered token can be re-registered later on, possibly with a different Asset Manager. * * Emits a `TokensDeregistered` event. */ function deregisterTokens(bytes32 poolId, IERC20[] memory tokens) external; /** * @dev Emitted when a Pool deregisters tokens by calling `deregisterTokens`. */ event TokensDeregistered(bytes32 indexed poolId, IERC20[] tokens); /** * @dev Returns detailed information for a Pool's registered token. * * `cash` is the number of tokens the Vault currently holds for the Pool. `managed` is the number of tokens * withdrawn and held outside the Vault by the Pool's token Asset Manager. The Pool's total balance for `token` * equals the sum of `cash` and `managed`. * * Internally, `cash` and `managed` are stored using 112 bits. No action can ever cause a Pool's token `cash`, * `managed` or `total` balance to be greater than 2^112 - 1. * * `lastChangeBlock` is the number of the block in which `token`'s total balance was last modified (via either a * join, exit, swap, or Asset Manager update). This value is useful to avoid so-called 'sandwich attacks', for * example when developing price oracles. A change of zero (e.g. caused by a swap with amount zero) is considered a * change for this purpose, and will update `lastChangeBlock`. * * `assetManager` is the Pool's token Asset Manager. */ function getPoolTokenInfo(bytes32 poolId, IERC20 token) external view returns ( uint256 cash, uint256 managed, uint256 lastChangeBlock, address assetManager ); /** * @dev Returns a Pool's registered tokens, the total balance for each, and the latest block when *any* of * the tokens' `balances` changed. * * The order of the `tokens` array is the same order that will be used in `joinPool`, `exitPool`, as well as in all * Pool hooks (where applicable). Calls to `registerTokens` and `deregisterTokens` may change this order. * * If a Pool only registers tokens once, and these are sorted in ascending order, they will be stored in the same * order as passed to `registerTokens`. * * Total balances include both tokens held by the Vault and those withdrawn by the Pool's Asset Managers. These are * the amounts used by joins, exits and swaps. For a detailed breakdown of token balances, use `getPoolTokenInfo` * instead. */ function getPoolTokens(bytes32 poolId) external view returns ( IERC20[] memory tokens, uint256[] memory balances, uint256 lastChangeBlock ); /** * @dev Called by users to join a Pool, which transfers tokens from `sender` into the Pool's balance. This will * trigger custom Pool behavior, which will typically grant something in return to `recipient` - often tokenized * Pool shares. * * If the caller is not `sender`, it must be an authorized relayer for them. * * The `assets` and `maxAmountsIn` arrays must have the same length, and each entry indicates the maximum amount * to send for each asset. The amounts to send are decided by the Pool and not the Vault: it just enforces * these maximums. * * If joining a Pool that holds WETH, it is possible to send ETH directly: the Vault will do the wrapping. To enable * this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead of the * WETH address. Note that it is not possible to combine ETH and WETH in the same join. Any excess ETH will be sent * back to the caller (not the sender, which is important for relayers). * * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when * interacting with Pools that register and deregister tokens frequently. If sending ETH however, the array must be * sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the final * `assets` array might not be sorted. Pools with no registered tokens cannot be joined. * * If `fromInternalBalance` is true, the caller's Internal Balance will be preferred: ERC20 transfers will only * be made for the difference between the requested amount and Internal Balance (if any). Note that ETH cannot be * withdrawn from Internal Balance: attempting to do so will trigger a revert. * * This causes the Vault to call the `IBasePool.onJoinPool` hook on the Pool's contract, where Pools implement * their own custom logic. This typically requires additional information from the user (such as the expected number * of Pool shares). This can be encoded in the `userData` argument, which is ignored by the Vault and passed * directly to the Pool's contract, as is `recipient`. * * Emits a `PoolBalanceChanged` event. */ function joinPool( bytes32 poolId, address sender, address recipient, JoinPoolRequest memory request ) external payable; struct JoinPoolRequest { IAsset[] assets; uint256[] maxAmountsIn; bytes userData; bool fromInternalBalance; } /** * @dev Called by users to exit a Pool, which transfers tokens from the Pool's balance to `recipient`. This will * trigger custom Pool behavior, which will typically ask for something in return from `sender` - often tokenized * Pool shares. The amount of tokens that can be withdrawn is limited by the Pool's `cash` balance (see * `getPoolTokenInfo`). * * If the caller is not `sender`, it must be an authorized relayer for them. * * The `tokens` and `minAmountsOut` arrays must have the same length, and each entry in these indicates the minimum * token amount to receive for each token contract. The amounts to send are decided by the Pool and not the Vault: * it just enforces these minimums. * * If exiting a Pool that holds WETH, it is possible to receive ETH directly: the Vault will do the unwrapping. To * enable this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead * of the WETH address. Note that it is not possible to combine ETH and WETH in the same exit. * * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when * interacting with Pools that register and deregister tokens frequently. If receiving ETH however, the array must * be sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the * final `assets` array might not be sorted. Pools with no registered tokens cannot be exited. * * If `toInternalBalance` is true, the tokens will be deposited to `recipient`'s Internal Balance. Otherwise, * an ERC20 transfer will be performed. Note that ETH cannot be deposited to Internal Balance: attempting to * do so will trigger a revert. * * `minAmountsOut` is the minimum amount of tokens the user expects to get out of the Pool, for each token in the * `tokens` array. This array must match the Pool's registered tokens. * * This causes the Vault to call the `IBasePool.onExitPool` hook on the Pool's contract, where Pools implement * their own custom logic. This typically requires additional information from the user (such as the expected number * of Pool shares to return). This can be encoded in the `userData` argument, which is ignored by the Vault and * passed directly to the Pool's contract. * * Emits a `PoolBalanceChanged` event. */ function exitPool( bytes32 poolId, address sender, address payable recipient, ExitPoolRequest memory request ) external; struct ExitPoolRequest { IAsset[] assets; uint256[] minAmountsOut; bytes userData; bool toInternalBalance; } /** * @dev Emitted when a user joins or exits a Pool by calling `joinPool` or `exitPool`, respectively. */ event PoolBalanceChanged( bytes32 indexed poolId, address indexed liquidityProvider, IERC20[] tokens, int256[] deltas, uint256[] protocolFeeAmounts ); enum PoolBalanceChangeKind { JOIN, EXIT } // Swaps // // Users can swap tokens with Pools by calling the `swap` and `batchSwap` functions. To do this, // they need not trust Pool contracts in any way: all security checks are made by the Vault. They must however be // aware of the Pools' pricing algorithms in order to estimate the prices Pools will quote. // // The `swap` function executes a single swap, while `batchSwap` can perform multiple swaps in sequence. // In each individual swap, tokens of one kind are sent from the sender to the Pool (this is the 'token in'), // and tokens of another kind are sent from the Pool to the recipient in exchange (this is the 'token out'). // More complex swaps, such as one token in to multiple tokens out can be achieved by batching together // individual swaps. // // There are two swap kinds: // - 'given in' swaps, where the amount of tokens in (sent to the Pool) is known, and the Pool determines (via the // `onSwap` hook) the amount of tokens out (to send to the recipient). // - 'given out' swaps, where the amount of tokens out (received from the Pool) is known, and the Pool determines // (via the `onSwap` hook) the amount of tokens in (to receive from the sender). // // Additionally, it is possible to chain swaps using a placeholder input amount, which the Vault replaces with // the calculated output of the previous swap. If the previous swap was 'given in', this will be the calculated // tokenOut amount. If the previous swap was 'given out', it will use the calculated tokenIn amount. These extended // swaps are known as 'multihop' swaps, since they 'hop' through a number of intermediate tokens before arriving at // the final intended token. // // In all cases, tokens are only transferred in and out of the Vault (or withdrawn from and deposited into Internal // Balance) after all individual swaps have been completed, and the net token balance change computed. This makes // certain swap patterns, such as multihops, or swaps that interact with the same token pair in multiple Pools, cost // much less gas than they would otherwise. // // It also means that under certain conditions it is possible to perform arbitrage by swapping with multiple // Pools in a way that results in net token movement out of the Vault (profit), with no tokens being sent in (only // updating the Pool's internal accounting). // // To protect users from front-running or the market changing rapidly, they supply a list of 'limits' for each token // involved in the swap, where either the maximum number of tokens to send (by passing a positive value) or the // minimum amount of tokens to receive (by passing a negative value) is specified. // // Additionally, a 'deadline' timestamp can also be provided, forcing the swap to fail if it occurs after // this point in time (e.g. if the transaction failed to be included in a block promptly). // // If interacting with Pools that hold WETH, it is possible to both send and receive ETH directly: the Vault will do // the wrapping and unwrapping. To enable this mechanism, the IAsset sentinel value (the zero address) must be // passed in the `assets` array instead of the WETH address. Note that it is possible to combine ETH and WETH in the // same swap. Any excess ETH will be sent back to the caller (not the sender, which is relevant for relayers). // // Finally, Internal Balance can be used when either sending or receiving tokens. enum SwapKind { GIVEN_IN, GIVEN_OUT } /** * @dev Performs a swap with a single Pool. * * If the swap is 'given in' (the number of tokens to send to the Pool is known), it returns the amount of tokens * taken from the Pool, which must be greater than or equal to `limit`. * * If the swap is 'given out' (the number of tokens to take from the Pool is known), it returns the amount of tokens * sent to the Pool, which must be less than or equal to `limit`. * * Internal Balance usage and the recipient are determined by the `funds` struct. * * Emits a `Swap` event. */ function swap( SingleSwap memory singleSwap, FundManagement memory funds, uint256 limit, uint256 deadline ) external payable returns (uint256); /** * @dev Data for a single swap executed by `swap`. `amount` is either `amountIn` or `amountOut` depending on * the `kind` value. * * `assetIn` and `assetOut` are either token addresses, or the IAsset sentinel value for ETH (the zero address). * Note that Pools never interact with ETH directly: it will be wrapped to or unwrapped from WETH by the Vault. * * The `userData` field is ignored by the Vault, but forwarded to the Pool in the `onSwap` hook, and may be * used to extend swap behavior. */ struct SingleSwap { bytes32 poolId; SwapKind kind; IAsset assetIn; IAsset assetOut; uint256 amount; bytes userData; } /** * @dev Performs a series of swaps with one or multiple Pools. In each individual swap, the caller determines either * the amount of tokens sent to or received from the Pool, depending on the `kind` value. * * Returns an array with the net Vault asset balance deltas. Positive amounts represent tokens (or ETH) sent to the * Vault, and negative amounts represent tokens (or ETH) sent by the Vault. Each delta corresponds to the asset at * the same index in the `assets` array. * * Swaps are executed sequentially, in the order specified by the `swaps` array. Each array element describes a * Pool, the token to be sent to this Pool, the token to receive from it, and an amount that is either `amountIn` or * `amountOut` depending on the swap kind. * * Multihop swaps can be executed by passing an `amount` value of zero for a swap. This will cause the amount in/out * of the previous swap to be used as the amount in for the current one. In a 'given in' swap, 'tokenIn' must equal * the previous swap's `tokenOut`. For a 'given out' swap, `tokenOut` must equal the previous swap's `tokenIn`. * * The `assets` array contains the addresses of all assets involved in the swaps. These are either token addresses, * or the IAsset sentinel value for ETH (the zero address). Each entry in the `swaps` array specifies tokens in and * out by referencing an index in `assets`. Note that Pools never interact with ETH directly: it will be wrapped to * or unwrapped from WETH by the Vault. * * Internal Balance usage, sender, and recipient are determined by the `funds` struct. The `limits` array specifies * the minimum or maximum amount of each token the vault is allowed to transfer. * * `batchSwap` can be used to make a single swap, like `swap` does, but doing so requires more gas than the * equivalent `swap` call. * * Emits `Swap` events. */ function batchSwap( SwapKind kind, BatchSwapStep[] memory swaps, IAsset[] memory assets, FundManagement memory funds, int256[] memory limits, uint256 deadline ) external payable returns (int256[] memory); /** * @dev Data for each individual swap executed by `batchSwap`. The asset in and out fields are indexes into the * `assets` array passed to that function, and ETH assets are converted to WETH. * * If `amount` is zero, the multihop mechanism is used to determine the actual amount based on the amount in/out * from the previous swap, depending on the swap kind. * * The `userData` field is ignored by the Vault, but forwarded to the Pool in the `onSwap` hook, and may be * used to extend swap behavior. */ struct BatchSwapStep { bytes32 poolId; uint256 assetInIndex; uint256 assetOutIndex; uint256 amount; bytes userData; } /** * @dev Emitted for each individual swap performed by `swap` or `batchSwap`. */ event Swap( bytes32 indexed poolId, IERC20 indexed tokenIn, IERC20 indexed tokenOut, uint256 amountIn, uint256 amountOut ); /** * @dev All tokens in a swap are either sent from the `sender` account to the Vault, or from the Vault to the * `recipient` account. * * If the caller is not `sender`, it must be an authorized relayer for them. * * If `fromInternalBalance` is true, the `sender`'s Internal Balance will be preferred, performing an ERC20 * transfer for the difference between the requested amount and the User's Internal Balance (if any). The `sender` * must have allowed the Vault to use their tokens via `IERC20.approve()`. This matches the behavior of * `joinPool`. * * If `toInternalBalance` is true, tokens will be deposited to `recipient`'s internal balance instead of * transferred. This matches the behavior of `exitPool`. * * Note that ETH cannot be deposited to or withdrawn from Internal Balance: attempting to do so will trigger a * revert. */ struct FundManagement { address sender; bool fromInternalBalance; address payable recipient; bool toInternalBalance; } /** * @dev Simulates a call to `batchSwap`, returning an array of Vault asset deltas. Calls to `swap` cannot be * simulated directly, but an equivalent `batchSwap` call can and will yield the exact same result. * * Each element in the array corresponds to the asset at the same index, and indicates the number of tokens (or ETH) * the Vault would take from the sender (if positive) or send to the recipient (if negative). The arguments it * receives are the same that an equivalent `batchSwap` call would receive. * * Unlike `batchSwap`, this function performs no checks on the sender or recipient field in the `funds` struct. * This makes it suitable to be called by off-chain applications via eth_call without needing to hold tokens, * approve them for the Vault, or even know a user's address. * * Note that this function is not 'view' (due to implementation details): the client code must explicitly execute * eth_call instead of eth_sendTransaction. */ function queryBatchSwap( SwapKind kind, BatchSwapStep[] memory swaps, IAsset[] memory assets, FundManagement memory funds ) external returns (int256[] memory assetDeltas); // Flash Loans /** * @dev Performs a 'flash loan', sending tokens to `recipient`, executing the `receiveFlashLoan` hook on it, * and then reverting unless the tokens plus a proportional protocol fee have been returned. * * The `tokens` and `amounts` arrays must have the same length, and each entry in these indicates the loan amount * for each token contract. `tokens` must be sorted in ascending order. * * The 'userData' field is ignored by the Vault, and forwarded as-is to `recipient` as part of the * `receiveFlashLoan` call. * * Emits `FlashLoan` events. */ function flashLoan( IFlashLoanRecipient recipient, IERC20[] memory tokens, uint256[] memory amounts, bytes memory userData ) external; /** * @dev Emitted for each individual flash loan performed by `flashLoan`. */ event FlashLoan(IFlashLoanRecipient indexed recipient, IERC20 indexed token, uint256 amount, uint256 feeAmount); // Asset Management // // Each token registered for a Pool can be assigned an Asset Manager, which is able to freely withdraw the Pool's // tokens from the Vault, deposit them, or assign arbitrary values to its `managed` balance (see // `getPoolTokenInfo`). This makes them extremely powerful and dangerous. Even if an Asset Manager only directly // controls one of the tokens in a Pool, a malicious manager could set that token's balance to manipulate the // prices of the other tokens, and then drain the Pool with swaps. The risk of using Asset Managers is therefore // not constrained to the tokens they are managing, but extends to the entire Pool's holdings. // // However, a properly designed Asset Manager smart contract can be safely used for the Pool's benefit, // for example by lending unused tokens out for interest, or using them to participate in voting protocols. // // This concept is unrelated to the IAsset interface. /** * @dev Performs a set of Pool balance operations, which may be either withdrawals, deposits or updates. * * Pool Balance management features batching, which means a single contract call can be used to perform multiple * operations of different kinds, with different Pools and tokens, at once. * * For each operation, the caller must be registered as the Asset Manager for `token` in `poolId`. */ function managePoolBalance(PoolBalanceOp[] memory ops) external; struct PoolBalanceOp { PoolBalanceOpKind kind; bytes32 poolId; IERC20 token; uint256 amount; } /** * Withdrawals decrease the Pool's cash, but increase its managed balance, leaving the total balance unchanged. * * Deposits increase the Pool's cash, but decrease its managed balance, leaving the total balance unchanged. * * Updates don't affect the Pool's cash balance, but because the managed balance changes, it does alter the total. * The external amount can be either increased or decreased by this call (i.e., reporting a gain or a loss). */ enum PoolBalanceOpKind { WITHDRAW, DEPOSIT, UPDATE } /** * @dev Emitted when a Pool's token Asset Manager alters its balance via `managePoolBalance`. */ event PoolBalanceManaged( bytes32 indexed poolId, address indexed assetManager, IERC20 indexed token, int256 cashDelta, int256 managedDelta ); // Protocol Fees // // Some operations cause the Vault to collect tokens in the form of protocol fees, which can then be withdrawn by // permissioned accounts. // // There are two kinds of protocol fees: // // - flash loan fees: charged on all flash loans, as a percentage of the amounts lent. // // - swap fees: a percentage of the fees charged by Pools when performing swaps. For a number of reasons, including // swap gas costs and interface simplicity, protocol swap fees are not charged on each individual swap. Rather, // Pools are expected to keep track of how much they have charged in swap fees, and pay any outstanding debts to the // Vault when they are joined or exited. This prevents users from joining a Pool with unpaid debt, as well as // exiting a Pool in debt without first paying their share. /** * @dev Returns the current protocol fee module. */ function getProtocolFeesCollector() external view returns (ProtocolFeesCollector); /** * @dev Safety mechanism to pause most Vault operations in the event of an emergency - typically detection of an * error in some part of the system. * * The Vault can only be paused during an initial time period, after which pausing is forever disabled. * * While the contract is paused, the following features are disabled: * - depositing and transferring internal balance * - transferring external balance (using the Vault's allowance) * - swaps * - joining Pools * - Asset Manager interactions * * Internal Balance can still be withdrawn, and Pools exited. */ function setPaused(bool paused) external; /** * @dev Returns the Vault's WETH instance. */ function WETH() external view returns (IWETH); // solhint-disable-previous-line func-name-mixedcase }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.7.0; import "../../lib/openzeppelin/IERC20.sol"; /** * @dev Interface for the WETH token contract used internally for wrapping and unwrapping, to support * sending and receiving ETH in joins, swaps, and internal balance deposits and withdrawals. */ interface IWETH is IERC20 { function deposit() external payable; function withdraw(uint256 amount) external; }
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved // // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.7.6; /// @notice Interface for managing list of addresses permitted to perform preferred rate /// arbitrage swaps on Cron-Fi TWAMM V1.0. /// interface IArbitrageurList { /// @param sender is the address that called the function changing list owner permissions. /// @param listOwner is the address to change list owner permissions on. /// @param permission is true if the address specified in listOwner is granted list owner /// permissions. Is false otherwise. /// event ListOwnerPermissions(address indexed sender, address indexed listOwner, bool indexed permission); /// @param sender is the address that called the function changing arbitrageur permissions. /// @param arbitrageurs is a list of addresses to change arbitrage permissions on. /// @param permission is true if the addresses specified in arbitrageurs is granted /// arbitrage permissions. Is false otherwise. /// event ArbitrageurPermissions(address indexed sender, address[] arbitrageurs, bool indexed permission); /// @param sender is the address that called the function changing the next list address. /// @param nextListAddress is the address the return value of the nextList function is set to. /// event NextList(address indexed sender, address indexed nextListAddress); /// @notice Returns true if the provide address is permitted the preferred /// arbitrage rate in the partner swap method of a Cron-Fi TWAMM pool. /// Returns false otherwise. /// @param _address the address to check for arbitrage rate permissions. /// function isArbitrageur(address _address) external returns (bool); /// @notice Returns the address of the next contract implementing the next list of arbitrageurs. /// If the return value is the NULL address, address(0), then the TWAMM contract's update /// list method will keep the existing address it is storing to check for arbitrage permissions. /// function nextList() external returns (address); }
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved // // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; pragma experimental ABIEncoderV2; import { ICronV1FactoryOwnerActions } from "./pool/ICronV1FactoryOwnerActions.sol"; import { ICronV1PoolAdminActions } from "./pool/ICronV1PoolAdminActions.sol"; import { ICronV1PoolArbitrageurActions } from "./pool/ICronV1PoolArbitrageurActions.sol"; import { ICronV1PoolEnums } from "./pool/ICronV1PoolEnums.sol"; import { ICronV1PoolEvents } from "./pool/ICronV1PoolEvents.sol"; import { ICronV1PoolHelpers } from "./pool/ICronV1PoolHelpers.sol"; import { IERC20 } from "../balancer-core-v2/lib/openzeppelin/IERC20.sol"; interface ICronV1Pool is ICronV1FactoryOwnerActions, ICronV1PoolAdminActions, ICronV1PoolArbitrageurActions, ICronV1PoolEnums, ICronV1PoolEvents, ICronV1PoolHelpers, IERC20 {}
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved // // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.7.6; import { ICronV1Pool } from "../interfaces/ICronV1Pool.sol"; interface ICronV1PoolFactory { /// @notice This event tracks pool creations from this factory /// @param pool the address of the pool /// @param token0 The token 0 in this pool /// @param token1 The token 1 in this pool /// @param poolType The poolType set for this pool event CronV1PoolCreated( address indexed pool, address indexed token0, address indexed token1, ICronV1Pool.PoolType poolType ); /// @notice This event tracks pool being set from this factory /// @param pool the address of the pool /// @param token0 The token 0 in this pool /// @param token1 The token 1 in this pool /// @param poolType The poolType set for this pool event CronV1PoolSet( address indexed pool, address indexed token0, address indexed token1, ICronV1Pool.PoolType poolType ); /// @notice This event tracks pool deletions from this factory /// @param pool the address of the pool /// @param token0 The token 0 in this pool /// @param token1 The token 1 in this pool /// @param poolType The poolType set for this pool event CronV1PoolRemoved( address indexed pool, address indexed token0, address indexed token1, ICronV1Pool.PoolType poolType ); /// @notice This event tracks pool creations from this factory /// @param oldAdmin the address of the previous admin /// @param newAdmin the address of the new admin event OwnerChanged(address indexed oldAdmin, address indexed newAdmin); // Functions function create( address _token0, address _token1, string memory _name, string memory _symbol, uint256 _poolType ) external returns (address); function set( address _token0, address _token1, uint256 _poolType, address _pool ) external; function remove( address _token0, address _token1, uint256 _poolType ) external; function transferOwnership( address _newOwner, bool _direct, bool _renounce ) external; function claimOwnership() external; function owner() external view returns (address); function pendingOwner() external view returns (address); function getPool( address _token0, address _token1, uint256 _poolType ) external view returns (address pool); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity >=0.7.6; import { IERC20 } from "../balancer-core-v2/lib/openzeppelin/IERC20.sol"; interface IERC20Decimals is IERC20 { // Non standard but almost all erc20 have this function decimals() external view returns (uint8); }
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved // // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.7.6; /// @dev Conventions in the methods, variables and constants are as follows: /// /// Prefixes: /// /// - In constants, the prefix "Sn", where 1 <= n <= 4, denotes which slot the constant /// pertains too. There are four storage slots that are bitpacked. For example, /// "S2_OFFSET_ORACLE_TIMESTAMP" refers to the offset of the oracle timestamp in bit- /// packed storage slot 2. /// /// Suffixes: /// /// - The suffix of a variable name denotes the type contained within the variable. /// For instance "uint256 _incrementU96" is a 256-bit unsigned container representing /// the 96-bit value "_increment". /// In the case of "uint256 _balancerFeeDU1F18", the 256-bit unsigned container is /// representing a 19 digit decimal value with 18 fractional digits. In this scenario, /// the D=Decimal, U=Unsigned, F=Fractional. /// Finally, "uint128 valueU128F64" is a 128-bit container representing a 128-bit value /// with 64 fractional bits. /// /// - The suffix of a function name denotes what slot it is proprietary too as a /// matter of convention. While unchecked at run-time or by the compiler, the naming /// convention easily aids in understanding what slot a packed value is stored within. /// For instance the function "unpackFeeShiftS3" unpacks the fee shift from slot 3. If /// the value of slot 2 were passed to this method, the unpacked value would be /// incorrect. // // Structs Related to Virtual Orders //////////////////////////////////////////////////////////////////////////////// /// @notice Virtual Order details for a single user's Long-Term (LT) swap. An LT swap from /// Token0 to Token1 is described as a user selling Token0 to the pool to buy Token1 /// from the pool. Vice-versa if the swap is from Token1 to Token0. /// @member token0To1 Swap direction, true swapping Token0 for Token1. False otherwise. /// @member salesRate Amount of token sold to the pool per block for LT swap duration. /// @member scaledProceedsAtSubmissionU128 The normalized proceeds of the pool for the token /// being purchased at the block the order is /// submitted. For example, for an LT swap of Token0 /// for Token1, this value would be the normalized /// proceeds of Token1 for the pool. The normalized /// value is also scaled for precision reasons. /// Min. = 0, Max. = (2**128) - 1 /// @member owner The address issuing the LT swap virtual order; exclusively able to cancel or /// withdraw the order. /// @member delegate Is an address that is able to withdraw or cancel the LT swap on behalf /// of owner account, as long as the recipient specified is the owner /// account. /// @member orderExpiry is the block in which this order expires. struct Order { bool token0To1; uint112 salesRate; uint128 scaledProceedsAtSubmissionU128; address owner; address delegate; uint256 orderExpiry; } /// @notice This struct abstracts two order pools representing pooled Long-Term (LT) swaps in /// each swap direction along with the current proceeds and a mapping of the sales /// rate of each token at the end of a block. This allows the grouping of swaps in /// the two swap directions for gas efficient execution when virutal orders are /// executed. It is an adaptation of the staking algorithm desribed here: /// - https://uploads-ssl.webflow.com/5ad71ffeb79acc67c8bcdaba/5ad8d1193a40977462982470_scalable-reward-distribution-paper.pdf /// @member currentSalesRates stores the current sales rate of both Token0 and Token1 per block /// as 112-bit numbers packed into the 256-bit container. Token0 /// occupies bits 224 downto 113 and Token1 bits 112 downto 1. /// @member scaledProceeds stores the normalized, scaled, proceeds of each order pool together as /// 128-bit numbers packed into the 256-bit container. Scaled proceeds0 /// occupies bits 256 downto 129 and scaled proceeds1 occupies /// bits 128 downto 1. /// WARNING: Scaled proceeds0 and scaled proceeds1 described above are /// not the proceeds of Token0 and Token1 as would be expected, but rather /// the proceeds of order pool 0 and order pool 1 respectively. This means /// that scaled proceeds0 is actually the amount of Token1 obtained for /// users selling Token0 to the pool and vice-versa for proceeds1. /// @member salesRateEndingPerBlock is a mapping of a block number to the sales rates of Token0 /// and Token1 expiring on that block number for each order pool. /// The 112-bit sales rates are stored in a single 256-bit slot /// together for efficiency. The sales rate for Token0 occupies /// bits 224 downto 113 while the sales rate for Token1 occupies /// bits 112 townto 1. /// struct OrderPools { uint256 currentSalesRates; uint256 scaledProceeds; mapping(uint256 => uint256) salesRatesEndingPerBlock; } /// @notice This struct contains the order pool data for virtual orders comprising of sales of /// Token0 for Token1 and vice-versa over multiple blocks. Each order pool is stored /// herein, tracking the current sales rates and proceeds along with expiring sales /// rates. /// This struct also stores the scaled proceeds at each block, allowing an individual /// user's proceeds to be calculated for a given interval. Each user's order is stored /// with a mapping to their order id and the most recently executed virtual order block /// and next order id are also stored herein. /// @member orderPools is a struct containing the sale rate and proceeds for each of the two /// order pools along with expiring orders sales rates mapped by block. /// @member scaledProceedsAtBlock is a mapping of a block number to the normalized, scaled, /// proceeds of each order pool together as 128-bit numbers packed /// into the 256-bit container. Scaled proceeds0 occupies /// bits 256 downto 129 and scaled proceeds1 occupies /// bits 128 downto 1. /// WARNING: Scaled proceeds0 and scaled proceeds1 described above are /// not the proceeds of Token0 and Token1 as would be expected, but rather /// the proceeds of order pool 0 and order pool 1 respectively. This means /// that scaled proceeds0 is actually the amount of Token1 obtained for /// users selling Token0 to the pool and vice-versa for proceeds1. /// @dev The values contained in scaledProceedsAtBlock are always increasing and are expected to /// overflow. Their difference when measured between two blocks, determines the proceeds in a /// particular time-interval. A user's sales rate multiplying that amount determines the user's /// share of the proceeds (scaledProceeds are normalized by the total sales rate and scaled up for /// maintaining precision). The subtraction of the two points is also expected to underflow. /// @member orderMap maps a particular order id to information about that order. /// @member lastVirtualOrderBlock The ethereum block number before the last virtual orders were executed. /// @member nextOrderId Is the next order id issued when a user places a Long-Term swap virtual order. /// struct VirtualOrders { OrderPools orderPools; mapping(uint256 => uint256) scaledProceedsAtBlock; mapping(uint256 => Order) orderMap; uint256 lastVirtualOrderBlock; uint256 nextOrderId; } // // Structs Related to Other Pool Features //////////////////////////////////////////////////////////////////////////////// /// @notice The cumulative prices of Token0 and Token1 as of the start of the /// last executed block (the timestamp of which can be fetched using /// getOracleTimeStamp). /// @member token0U256F112 The cumulative price of Token0 measured in amount of /// Token1 seconds. /// @member token1U256F112 The cumulative price of Token1 measured in amount of /// Token0 seconds. /// @dev These values have 112 fractional bits and are expected to overflow. /// Behavior is identical to the price oracle introduced in Uniswap V2 with /// similar limitations and vulnerabilities. /// @dev The average price over an interval can be obtained by sampling these /// values and their measurement times (see getOracleTimeStamp) and /// computing the difference over the given interval. struct PriceOracle { uint256 token0U256F112; uint256 token1U256F112; } // // Structs for Gas Efficiency / Stack Depth Limitations //////////////////////////////////////////////////////////////////////////////// /// @notice Struct for executing virtual orders across functions efficiently. /// @member token0ReserveU112 reserves of Token0 in the TWAMM pool. /// @member token1ReserveU112 reserves of Token1 in the TWAMM pool. /// @member lpFeeU60 This is the portion of fees to be distributed to Liquidity Providers /// (LPs) after Balancer's portion is collected. The portioning is based /// on fractions of 10**18 and the value is computed by subtracting /// Balancer's portion from 10**18. If Cron-Fi fees are being collected /// this value is used to compute the fee share, feeShareU60. /// @member feeShareU60 If Cron-Fi fees are being collected, this amount represents a /// single share of the fees remaining after Balancer's portion. A /// single share goes to Cron-Fi and multiples of a single share go /// to the Liquidity Providers (LPs) based on the fee shift value, /// feeShiftU3. /// @member feeShiftU3 If Cron-Fi fees are being collected, this represents the amount of /// bits shifted to partition fees between Liquidity Providers (LPs) /// and Cron-Fi. For example, if this is 1, then 2 shares of fees /// collected after Balancer's portion go to the LPs and 1 share goes /// to Cron-Fi. If it is 2, then 4 shares go to the LPs and 1 share /// goes to Cron-Fi. /// @member orderPool0ProceedsScaling is the amount to scale proceeds of order pool 0 (Long- /// Term swaps of Token 0 to Token 1) based on the number /// of decimal places in Token 0. /// @member orderPool0ProceedsScaling is the amount to scale proceeds of order pool 1 (Long- /// Term swaps of Token 1 to Token 0) based on the number /// of decimal places in Token 1. /// @member token0BalancerFeesU96 Balancer fees collected for Token0-->Token1 swaps. /// @member token1BalancerFeesU96 Balancer fees collected for Token1-->Token0 swaps. /// @member token0CronFiFeesU96 Cron-Fi fees collected for Token0-->Token1 Long-Term swaps. /// @member token1CronFiFeesU96 Cron-Fi fees collected for Token1-->Token0 Long-Term swaps. /// @member token0OrdersU112 Amount of Token0 sold to the pool in virtual orders. /// @member token1OrdersU112 Amount of Token1 sold to the pool in virtual orders. /// @member token0ProceedsU112 Amount of Token0 bought from the pool in virtual orders. /// @member token1ProceedsU112 Amount of Token1 bought from the pool in virtual orders. /// @member token0OracleU256F112 The computed increment for the price oracle for Token 0. /// @member token1OracleU256F112 The computed increment for the price oracle for Token 1. /// @member oracleTimeStampU32 The oracle time stamp. /// struct ExecVirtualOrdersMem { uint256 token0ReserveU112; uint256 token1ReserveU112; uint256 lpFeeU60; uint256 feeShareU60; uint256 feeShiftU3; uint256 token0BalancerFeesU96; uint256 token1BalancerFeesU96; uint256 token0CronFiFeesU96; uint256 token1CronFiFeesU96; uint256 token0OrdersU112; uint256 token1OrdersU112; uint256 token0ProceedsU112; uint256 token1ProceedsU112; uint256 token0OracleU256F112; uint256 token1OracleU256F112; } /// @notice Struct for executing the virtual order loop efficiently (reduce /// storage reads/writes). Advantages increase when pool is inactive /// for longer multiples of the Order Block Interval. /// @member lastVirtualOrderBlock The ethereum block number before the last virtual orders were /// executed. /// @member scaledProceeds0U128 The normalized scaled proceeds of order pool 0 in Token1. For /// example, for an LT swap of Token0 for Token1, this value /// would be the normalized proceeds of Token1 for the pool. The /// normalized value is also scaled for precision reasons. /// Min. = 0, Max. = (2**128) - 1 /// @member scaledProceeds1U128 The normalized scaled proceeds of order pool 1 in Token0. /// Min. = 0, Max. = (2**128) - 1 /// @member currentSalesRate0U112 The current sales rate of Token0 per block. /// Min. = 0, Max. = (2**112) - 1 /// @member currentSalesRate1U112 The current sales rate of Token1 per block. /// Min. = 0, Max. = (2**112) - 1 /// struct LoopMem { // Block Numbers: uint256 lastVirtualOrderBlock; // Order Pool Items: uint256 scaledProceeds0U128; uint256 scaledProceeds1U128; uint256 currentSalesRate0U112; uint256 currentSalesRate1U112; }
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved // // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; interface ICronV1FactoryOwnerActions { function setAdminStatus(address _admin, bool _status) external; function setFeeAddress(address _feeDestination) external; function setFeeShift(uint256 _feeShift) external; function setCollectBalancerFees(bool _collectValue) external; }
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved // // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; interface ICronV1PoolAdminActions { function setPause(bool _pauseValue) external; function setParameter(uint256 _paramTypeU, uint256 _value) external; function setArbitragePartner(address _arbPartner, address _arbitrageList) external; }
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved // // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; interface ICronV1PoolArbitrageurActions { function updateArbitrageList() external returns (address); function executeVirtualOrdersToBlock(uint256 _maxBlock) external; }
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved // // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; interface ICronV1PoolEnums { /// @notice Enumeration for the type of TWAMM pool created; the type determines the default fees and the immutable block /// interval that the pool will operate with for it's lifetime. Each enumeration value is described in more /// detail below (Fee Points = FP): /// /// Stable: /// /// - Intended for pool tokens that trade frequently; features lower fees and more frequent Long-Term order /// expiries in exchange for higher gas use. /// Short-Term Swap Fee = 10 FP (0.010%) /// Arbitrageur Swap Fee = 5 FP (0.005%) /// Long-Term Swap Fee = 30 FP (0.030%) /// Order Block Interval = 75 blocks (~15 minutes) /// /// Liquid: /// /// - The middle ground setting between tokens that trade frequently and those that trade infrequently with /// low-liquidity. Mid-range fees and order expiry frequency. /// Short-Term Swap Fee = 50 FP (0.050%) /// Arbitrageur Swap Fee = 25 FP (0.025%) /// Long-Term Swap Fee = 150 FP (0.150%) /// Order Block Interval = 300 blocks (~1 hour) /// /// Volatile: /// /// - Intended for pool tokens that trade infrequently with low-liquidity; features higher fees and less /// frequent Long-Term order expiries in exchange for reduced gas use. /// Short-Term Swap Fee = 100 FP (0.100%) /// Arbitrageur Swap Fee = 50 FP (0.050%) /// Long-Term Swap Fee = 300 FP (0.300%) /// Order Block Interval = 1200 blocks (~ 4 hours) /// enum PoolType { Stable, // 0 Liquid, // 1 Volatile // 2 } /// @notice Enumeration for functionality when joining the pool: /// - Join performs the standard Join/Mint functionality, taking the provided tokens in exchange for /// pool Liquidity Provider (LP) tokens. /// - Reward performs a donation of the provided tokens to the pool with no LP tokens provided in return. /// enum JoinType { Join, // 0 Reward // 1 } /// @notice Enumeration for functionality when swapping with the pool: /// - RegularSwap performs a standard swap of the specified token for its opposing token using the Constant /// Product Automated Market Maker (CPAMM) formula. /// - LongTermSwap performs a swap of the spcified token for its opposing token over more than one block. /// - PartnerSwap performs a reduced fee RegularSwap with registered arbitrage partner's arbitrageurs. /// enum SwapType { RegularSwap, // 0 LongTermSwap, // 1 PartnerSwap // 2 } /// @notice Enumeration for functionality when exiting the pool: /// - Exit performs a standard exit or burn functionality, taking provided LP tokens in exchange for the /// proportional amount of pool tokens. /// - Withdraw performs a Long-Term swap order proceeds withdrawl. /// - Cancel performs a Long-Term swap order cancellation, remitting proceeds and refunding unspent deposits. /// - FeeWithdraw performs a withdraw of Cron-Fi fees to the fee address if enabled. /// enum ExitType { Exit, // 0 Withdraw, // 1 Cancel, // 2 FeeWithdraw // 3 } /// @notice Enumeration for shared parameterization setting function to specify parameter being set: /// - SwapFeeFP is the short term swap fee in Fee Points (FP). /// - PartnerFeeFP is the arbitrage partner swap fee in FP. /// - LongSwapFeeFP is the Long-Term swap fee in FP. /// @dev NOTE: Total FP = 100,000. Thus a fee portion is the number of FP out of 100,000. /// enum ParamType { // Slot 1: SwapFeeFP, // 0 PartnerFeeFP, // 1 LongSwapFeeFP // 2 } /// @notice Enumeration for shared event log for boolean parameter state changes. The event /// BoolParameterChange will contain one of the following enum values to indicate a /// change to the respective one--the pool's paused state or collection of balancer fees. /// enum BoolParamType { Paused, // 0 CollectBalancerFees // 1 } }
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved // // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; import { ICronV1PoolEnums } from "./ICronV1PoolEnums.sol"; interface ICronV1PoolEvents is ICronV1PoolEnums { /// @notice ShortTermSwap event is emitted for Short-Term (ST) swap transactions and /// arbitrage partner ST swap transactions. To differentiate, examine the value of /// swapType in the emitted event. /// event ShortTermSwap( address indexed sender, address indexed tokenIn, uint256 amountIn, uint256 amountOut, uint256 swapType ); /// @notice LongTermSwap event is emitted when Long-Term (LT) swaps transaction are issued to /// the pool. /// event LongTermSwap( address indexed sender, address indexed delegate, address indexed tokenIn, uint256 amountIn, uint256 intervals, uint256 orderId ); /// @notice PoolJoin events are emitted for Join/Mint and Reward transactions. A Reward /// transaction can be identified from a Join/Mint transaction by examining the /// emitted event's poolTokenAmt to see if is zero. /// event PoolJoin( address indexed sender, address indexed recipient, uint256 token0In, uint256 token1In, uint256 poolTokenAmt ); /// @notice WithdrawLongTermSwap events are emitted when an LT swap order is withdrawn or cancelled /// in a transaction. To differentiate between the two, only a cancellation will have non-zero /// values for refundOut. /// event WithdrawLongTermSwap( address indexed owner, address indexed refundToken, uint256 refundOut, address indexed proceedsToken, uint256 proceedsOut, uint256 orderId, address sender ); /// @notice FeeWithdraw events are emitted when Cron-Fi fees are withdrawn from the pool. /// event FeeWithdraw(address indexed sender, uint256 token0Out, uint256 token1Out); /// @notice PoolExit events are emitted when a Liquidity Provider (LP) redeems LP tokens for /// their share of tokens remaining in the pool. /// event PoolExit(address indexed sender, uint256 poolTokenAmt, uint256 token0Out, uint256 token1Out); /// @notice AdministratorStatusChange events are emitted when an administrator address, admin, /// is given administrator privileges (status == true) or when they are taken away /// (status == false). /// event AdministratorStatusChange(address indexed sender, address indexed admin, bool status); /// @notice ProtocolFeeTooLarge is emitted if the protocol fee passed in by balancer ever exceeds /// 1e18 (in which case the change is ignored and fees continue with the last good value). /// event ProtocolFeeTooLarge(uint256 suggestedProtocolFee); /// @notice ParameterChange is emitted when a parameter value is changed to value. Consult the /// enum ParmType for the parameter undergoing change. /// event ParameterChange(address indexed sender, ParamType paramType, uint256 value); /// @notice FeeAddressChange is emitted when the fee address, feeAddress, is changed. /// event FeeAddressChange(address indexed sender, address indexed feeAddress); /// @notice FeeShiftChange is emitted when the fee shift, feeShift is changed. /// event FeeShiftChange(address indexed sender, uint256 feeShift); /// @notice BoolParameterChange is emitted when a boolean value parameter is changed. Consult the /// enum BoolParmType for the parameter undergoing change. /// event BoolParameterChange(address indexed sender, BoolParamType boolParam, bool value); /// @notice UpdatedArbitragePartner is emitted when an arbitrage partner's arbitrageur list is /// updated to a new contract address. /// event UpdatedArbitragePartner(address indexed sender, address partner, address list); /// @notice UpdatedArbitrageList is emitted when an arbitrage partner's updates their arbitrageur /// list is to a new contract address through the updateArbitrageList function. /// event UpdatedArbitrageList(address indexed partner, address indexed oldList, address indexed newList); /// @notice ExecuteVirtualOrdersEvent is emitted on calls to executeVirtualOrdersToBlock. /// event ExecuteVirtualOrdersEvent(address indexed sender, uint256 block); }
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved // // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; pragma experimental ABIEncoderV2; import { ICronV1PoolEnums } from "./ICronV1PoolEnums.sol"; import { Order, PriceOracle, ExecVirtualOrdersMem } from "../Structs.sol"; interface ICronV1PoolHelpers { function getVirtualPriceOracle(uint256 _maxBlock) external returns ( uint256 timestamp, uint256 token0U256F112, uint256 token1U256F112, uint256 blockNumber ); function getVirtualReserves(uint256 _maxBlock, bool _paused) external returns ( uint256 blockNumber, uint256 token0ReserveU112, uint256 token1ReserveU112, uint256 token0OrdersU112, uint256 token1OrdersU112, uint256 token0ProceedsU112, uint256 token1ProceedsU112, uint256 token0BalancerFeesU96, uint256 token1BalancerFeesU96, uint256 token0CronFiFeesU96, uint256 token1CronFiFeesU96 ); // solhint-disable-next-line func-name-mixedcase function POOL_ID() external view returns (bytes32); // solhint-disable-next-line func-name-mixedcase function POOL_TYPE() external view returns (ICronV1PoolEnums.PoolType); function getPriceOracle() external view returns ( uint256 timestamp, uint256 token0U256F112, uint256 token1U256F112 ); function getOrderIds( address _owner, uint256 _offset, uint256 _maxResults ) external view returns ( uint256[] memory orderIds, uint256 numResults, uint256 totalResults ); function getOrder(uint256 _orderId) external view returns (Order memory order); function getOrderIdCount() external view returns (uint256 nextOrderId); function getSalesRates() external view returns (uint256 salesRate0U112, uint256 salesRate1U112); function getLastVirtualOrderBlock() external view returns (uint256 lastVirtualOrderBlock); function getSalesRatesEndingPerBlock(uint256 _blockNumber) external view returns (uint256 salesRateEndingPerBlock0U112, uint256 salesRateEndingPerBlock1U112); function getShortTermFeePoints() external view returns (uint256); function getPartnerFeePoints() external view returns (uint256); function getLongTermFeePoints() external view returns (uint256); function getOrderAmounts() external view returns (uint256 orders0U112, uint256 orders1U112); function getProceedAmounts() external view returns (uint256 proceeds0U112, uint256 proceeds1U112); function getFeeShift() external view returns (uint256); function getCronFeeAmounts() external view returns (uint256 cronFee0U96, uint256 cronFee1U96); function isPaused() external view returns (bool); function isCollectingCronFees() external view returns (bool); function isCollectingBalancerFees() external view returns (bool); function getBalancerFee() external view returns (uint256); function getBalancerFeeAmounts() external view returns (uint256 balFee0U96, uint256 balFee1U96); }
// (c) Copyright 2023, Bad Pumpkin Inc. All Rights Reserved // // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.7.6; import { C } from "./Constants.sol"; import { requireErrCode, CronErrors } from "./Errors.sol"; /// @notice Library for bit-packing generic and specific values pertinent to Cron-Fi TWAMM /// into storage-slots efficiently (both for gas use and contract size). /// /// @dev Many custom representations are used herein (i.e. non native word lengths) and there /// are a number of explicit checks against the maximum of these non native word lengths. /// Furthermore there are unchecked operations (this code targets Solidity 0.7.x which /// didn't yet feature implicit arithmetic checks or have the 'unchecked' block feature) /// in this library for reasons of efficiency or desired overflow. Wherever they appear /// they will be documented and accompanied with one of the following tags: /// - #unchecked /// - #overUnderFlowIntended /// Identified risks will be accompanied and described with the following tag: /// - #RISK /// /// @dev Generic shifting methods were eschewed because of their additional gas use. /// @dev Conventions in the methods below are as follows: /// /// Suffixes: /// /// - The suffix of a variable name denotes the type contained within the variable. /// For instance "uint256 _incrementU96" is a 256-bit unsigned container representing /// a 96-bit value, _increment. /// In the case of "uint256 _balancerFeeDU1F18", the 256-bit unsigned container is /// representing a 19 digit decimal value with 18 fractional digits. In this scenario, /// the D=Decimal, U=Unsigned, F=Fractional. /// /// - The suffix of a function name denotes what slot it is proprietary too as a /// matter of convention. While unchecked at run-time or by the compiler, the naming /// convention easily aids in understanding what slot a packed value is stored within. /// For instance the function "unpackFeeShiftS3" unpacks the fee shift from slot 3. If /// the value of slot 2 were passed to this method, the unpacked value would be /// incorrect. /// /// Bit-Numbering: /// /// - Bits are counted starting with the least-significant bit (LSB) from 1. Thus for /// a 256-bit slot, the most-significant bit (MSB) is bit 256 and the LSB is bit 1. /// /// /// Offsets: /// /// - Offsets are the distance from the LSB to the desired LSB of the word being /// placed within a slot. For instance, to store an 8-bit word in a 256-bit slot /// at bits 16 down to 9, an offset of 8-bits should be specified. /// /// /// Pairs /// /// - The following methods which operate upon pairs follow the convention that a /// pair consists of two same sized words, with word0 stored above word1 within /// a slot. For example, a pair of 96-bit words will be stored with word0 /// occupying bits 192 downto 97 and word1 occupying bits 96 downto 1. The following /// diagram depicts this scenario: /// /// bit-256 bit-192 bit-96 bit-1 /// | | | | /// v v v v /// /// MSB < I ??? II word0 II word1 I > LSB /// /// ^ ^ /// | | /// bit-193 bit-97 /// library BitPackingLib { // // Generic Packing Functions //////////////////////////////////////////////////////////////////////////////// /// @notice Packs bit _bitU1 into the provided 256-bit slot, _slot, at location _offsetU8 /// bits from the provided slot's LSB, bit 1. /// @param _slot A 256-bit container to pack bit _bitU1 within. /// @param _bitU1 A 1-bit value to pack into the provided slot. /// Min. = 0, Max. = 1. /// @param _offsetU8 The distance in bits from the provided slot's LSB to store _bitU1 at. /// Min. = 0, Max. = 255. /// @dev WARNING: No checks of _offsetU8 are performed for efficiency! /// @return slot The modified slot containing _bitU1 at bit position _offsetU8 + 1. /// function packBit( uint256 _slot, uint256 _bitU1, uint256 _offsetU8 ) internal pure returns (uint256 slot) { requireErrCode(_bitU1 <= C.MAX_U1, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ); uint256 mask = C.MAX_U1 << _offsetU8; slot = (_bitU1 != 0) ? (_slot | mask) : (_slot & (~mask)); } /// @notice Unpacks bitU1 from the provided 256-bit slot, _slot, at location _offsetU8 /// bits from the provided slot's LSB, bit 1. /// @param _slot A 256-bit container to unpack bitU1 from. /// @param _offsetU8 The distance in bits from the provided slot's LSB to unpack bitU1 from. /// Min. = 0, Max. = 255. /// @dev WARNING: No checks of _offsetU8 are performed for efficiency! /// @return bitU1 The 1-bit value unpacked from the provided slot. /// Min. = 0, Max. = 1. /// function unpackBit(uint256 _slot, uint256 _offsetU8) internal pure returns (uint256 bitU1) { bitU1 = ((_slot >> _offsetU8) & C.MAX_U1); } /// @notice Packs ten-bit word, _wordU10, into the provided 256-bit slot, _slot, at location /// _offsetU8 bits from the provided slot's LSB, bit 1. /// @param _slot A 256-bit container to pack bit _wordU10 within. /// @param _wordU10 A ten-bit word to pack into the provided slot. /// Min. = 0, Max. = (2**10)-1. /// @param _offsetU8 The distance in bits from the provided slot's LSB to store _bitU1 at. /// Min. = 0, Max. = 255. /// @dev WARNING: No checks of _offsetU8 are performed for efficiency! /// @return slot The modified slot containing _wordU10 at bit position _offsetU8 + 1. /// function packU10( uint256 _slot, uint256 _wordU10, uint256 _offsetU8 ) internal pure returns (uint256 slot) { requireErrCode(_wordU10 <= C.MAX_U10, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ); uint256 clearMask = ~(C.MAX_U10 << _offsetU8); uint256 setMask = _wordU10 << _offsetU8; slot = (_slot & clearMask) | setMask; } /// @notice Unpacks wordU10 from the provided 256-bit slot, _slot, at location _offsetU8 /// bits from the provided slot's LSB, bit 1. /// @param _slot A 256-bit container to unpack wordU10 from. /// @param _offsetU8 The distance in bits from the provided slot's LSB to unpack wordU10 from. /// Min. = 0, Max. = 255. /// @dev WARNING: No checks of _offsetU8 are performed for efficiency! /// @return wordU10 The ten-bit word unpacked from the provided slot. /// Min. = 0, Max. = (2**10)-1. function unpackU10(uint256 _slot, uint256 _offsetU8) internal pure returns (uint256 wordU10) { wordU10 = (_slot >> _offsetU8) & C.MAX_U10; } /// @notice Increments the 96-bit words, word0 and/or word1, stored within the provided /// 256-bit slot, _slot, by the values provided in _increment0U96 and _increment1U96 /// respectively. Importantly, if the increment results in overflow, the value /// will "clamp" to the maximum value (2**96)-1. /// @dev See the section on Pairs in the notes on Conventions to understand how the two /// words are stored within the provided slot. /// @param _slot A 256-bit container holding two 96-bit words, word0 and word1. /// @param _increment0U96 The amount to increment word0 by. /// Min. = 0, Max. = (2**96)-1. /// @param _increment1U96 The amount to increment word1 by. /// Min. = 0, Max. = (2**96)-1. /// @return slot The modified slot containing incremented values of word0 and/or word1. /// function incrementPairWithClampU96( uint256 _slot, uint256 _increment0U96, uint256 _increment1U96 ) internal pure returns (uint256 slot) { uint256 word0U96 = ((_slot >> 96) & C.MAX_U96); uint256 word1U96 = (_slot & C.MAX_U96); if (_increment0U96 > 0) { requireErrCode(C.MAX_U256 - word0U96 >= _increment0U96, CronErrors.OVERFLOW); // #unchecked // safe from overflow in increment below because of check above. word0U96 += _increment0U96; // Clamp the resulting value to (2**96)+1 on overflow of U96 beyond MAX_U96: if (word0U96 > C.MAX_U96) { word0U96 = C.MAX_U96; } } if (_increment1U96 > 0) { requireErrCode(C.MAX_U256 - word1U96 >= _increment1U96, CronErrors.OVERFLOW); // #unchecked // safe from overflow in increment below because of check above. word1U96 += _increment1U96; // Clamp the resulting value to (2**96)+1 on overflow of U96 beyond MAX_U96: if (word1U96 > C.MAX_U96) { word1U96 = C.MAX_U96; } } // NOTE: No need create set masks as _value*U96 above checked against MAX_U96/clamped. slot = (_slot & C.CLEAR_MASK_PAIR_U96) | (word0U96 << 96) | word1U96; } /// @notice Unpacks the two 96-bit values, word0 and word1, from the provided slot, _slot, /// returning them along with the provided slot modified to clear the values /// or word0 and word1 to zero. /// @dev See the section on Pairs in the notes on Conventions to understand how the two /// words are stored within the provided slot. /// @param _slot A 256-bit container holding two 96-bit words, word0 and word1. /// @return slot The modified slot containing cleared values of word0 and word1. /// @return word0U96 The value of word0 prior to clearing it. /// Min. = 0, Max. = (2**96)-1. /// @return word1U96 The value of word1 prior to clearing it. /// Min. = 0, Max. = (2**96)-1. /// function unpackAndClearPairU96(uint256 _slot) internal pure returns ( uint256 slot, uint256 word0U96, uint256 word1U96 ) { word0U96 = (_slot >> 96) & C.MAX_U96; word1U96 = _slot & C.MAX_U96; slot = _slot & C.CLEAR_MASK_PAIR_U96; } /// @notice Unpacks and returns the two 96-bit values, word0 and word1, from the provided slot. /// @dev See the section on Pairs in the notes on Conventions to understand how the two /// words are stored within the provided slot. /// @param _slot A 256-bit container holding two 96-bit words, word0 and word1. /// @return word0U96 The value of word0. /// Min. = 0, Max. = (2**96)-1. /// @return word1U96 The value of word1. /// Min. = 0, Max. = (2**96)-1. /// function unpackPairU96(uint256 _slot) internal pure returns (uint256 word0U96, uint256 word1U96) { word0U96 = (_slot >> 96) & C.MAX_U96; word1U96 = _slot & C.MAX_U96; } /// @notice Packs the two provided 112-bit words, word0 and word1, into the provided 256-bit /// slot, _slot. /// @dev See the section on Pairs in the notes on Conventions to understand how the two /// words are stored within the provided slot. /// @param _slot A 256-bit container holding two 112-bit words, word0 and word1. /// @param _word0U112 The value of word0 to pack. /// Min. = 0, Max. = (2**112)-1. /// @param _word1U112 The value of word1 to pack. /// Min. = 0, Max. = (2**112)-1. /// @return slot The modified slot containing the values of word0 and word1. /// function packPairU112( uint256 _slot, uint256 _word0U112, uint256 _word1U112 ) internal pure returns (uint256 slot) { requireErrCode(_word0U112 <= C.MAX_U112, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ); requireErrCode(_word1U112 <= C.MAX_U112, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ); slot = (_slot & C.CLEAR_MASK_PAIR_U112) | (_word0U112 << 112) | _word1U112; } /// @notice Unpacks and returns the two 112-bit values, word0 and word1, from the provided slot. /// @dev See the section on Pairs in the notes on Conventions to understand how the two /// words are stored within the provided slot. /// @param _slot A 256-bit container holding two 112-bit words, word0 and word1. /// @return word0U112 The value of word0. /// Min. = 0, Max. = (2**112)-1. /// @return word1U112 The value of word1. /// Min. = 0, Max. = (2**112)-1. /// function unpackPairU112(uint256 _slot) internal pure returns (uint256 word0U112, uint256 word1U112) { word0U112 = (_slot >> 112) & C.MAX_U112; word1U112 = _slot & C.MAX_U112; } /// @notice Increments the 112-bit words, word0 and/or word1, stored within the provided /// 256-bit slot, _slot, by the values provided in _increment0U112 and _increment1U112 /// respectively. Errors on overflow. /// @dev See the section on Pairs in the notes on Conventions to understand how the two /// words are stored within the provided slot. /// @param _slot A 256-bit container holding two 112-bit words, word0 and word1. /// @param _increment0U112 The amount to increment word0 by. /// Min. = 0, Max. = (2**112)-1. /// @param _increment1U112 The amount to increment word1 by. /// Min. = 0, Max. = (2**112)-1. /// @return slot The modified slot containing incremented values of word0 and/or word1. /// function incrementPairU112( uint256 _slot, uint256 _increment0U112, uint256 _increment1U112 ) internal pure returns (uint256 slot) { uint256 word0U112 = ((_slot >> 112) & C.MAX_U112); uint256 word1U112 = (_slot & C.MAX_U112); if (_increment0U112 > 0) { requireErrCode(C.MAX_U112 - word0U112 >= _increment0U112, CronErrors.OVERFLOW); // #unchecked // safe from overflow in increment below because of check above. word0U112 += _increment0U112; } if (_increment1U112 > 0) { requireErrCode(C.MAX_U112 - word1U112 >= _increment1U112, CronErrors.OVERFLOW); // #unchecked // safe from overflow in increment below because of check above. word1U112 += _increment1U112; } // NOTE: No need to create set masks as _value*U112 above checked against MAX_U112. slot = (_slot & C.CLEAR_MASK_PAIR_U112) | (word0U112 << 112) | word1U112; } /// @notice Decrements the 112-bit words, word0 and/or word1, stored within the provided /// 256-bit slot, _slot, by the values provided in _decrement0U112 and /// _decrement1U112 respectively. Errors on underflow. /// @dev See the section on Pairs in the notes on Conventions to understand how the two /// words are stored within the provided slot. /// @param _slot A 256-bit container holding two 112-bit words, word0 and word1. /// @param _decrement0U112 The amount to decrement word0 by. /// Min. = 0, Max. = (2**112)-1. /// @param _decrement1U112 The amount to decrement word1 by. /// Min. = 0, Max. = (2**112)-1. /// @return slot The modified slot containing decremented values of word0 and/or word1. /// function decrementPairU112( uint256 _slot, uint256 _decrement0U112, uint256 _decrement1U112 ) internal pure returns (uint256 slot) { uint256 word0U112 = ((_slot >> 112) & C.MAX_U112); uint256 word1U112 = (_slot & C.MAX_U112); if (_decrement0U112 > 0) { requireErrCode(word0U112 >= _decrement0U112, CronErrors.UNDERFLOW); // #unchecked // safe from underflow in decrement below because of check above. word0U112 -= _decrement0U112; } if (_decrement1U112 > 0) { requireErrCode(word1U112 >= _decrement1U112, CronErrors.UNDERFLOW); // #unchecked // safe from underflow in decrement below because of check above. word1U112 -= _decrement1U112; } // NOTE: No need to create set masks as _value*U112 above both at most MAX_U112 (correct by // construction--checked at creation/pack) and operation is subtraction with underflow // checked; slot = (_slot & C.CLEAR_MASK_PAIR_U112) | (word0U112 << 112) | word1U112; } /// @notice Unpacks and returns the specified 128-bit values, word0 or word1, from the provided slot, /// depending on the value of isWord0. /// @dev See the section on Pairs in the notes on Conventions to understand how the two /// words are stored within the provided slot. /// @param _slot A 256-bit container holding two 128-bit words, word0 and word1. /// @param _isWord0 Instructs this method to unpack the upper 128-bits corresponding to word0 when true. /// Otherwise the lower 128-bits, word1 are unpacked. /// @return wordU128 The value of word0. /// Min. = 0, Max. = (2**128)-1. /// function unpackU128(uint256 _slot, bool _isWord0) internal pure returns (uint256 wordU128) { wordU128 = _isWord0 ? _slot >> 128 : _slot & C.MAX_U128; } /// @notice Packs the two provided 128-bit words, word0 and word1, into a 256-bit slot. /// @dev See the section on Pairs in the notes on Conventions to understand how the two /// words are stored within the slot. /// @param _word0U128 The value of word0 to pack. /// Min. = 0, Max. = (2**128)-1. /// @param _word1U128 The value of word1 to pack. /// Min. = 0, Max. = (2**128)-1. /// @return slot A slot containing the 128-bit values word0 and word1. /// function packPairU128(uint256 _word0U128, uint256 _word1U128) internal pure returns (uint256 slot) { requireErrCode(_word0U128 <= C.MAX_U128, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ); requireErrCode(_word1U128 <= C.MAX_U128, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ); slot = (_word0U128 << 128) | _word1U128; } /// @notice Unpacks and returns the two 128-bit values, word0 and word1, from the provided slot. /// @dev See the section on Pairs in the notes on Conventions to understand how the two /// words are stored within the provided slot. /// @param _slot A 256-bit container holding two 128-bit words, word0 and word1. /// @return word0U128 The value of word0. /// Min. = 0, Max. = (2**128)-1. /// @return word1U128 The value of word1. /// Min. = 0, Max. = (2**128)-1. /// function unpackPairU128(uint256 _slot) internal pure returns (uint256 word0U128, uint256 word1U128) { word0U128 = _slot >> 128; word1U128 = _slot & C.MAX_U128; } // // Slot 2 Specific Packing Functions //////////////////////////////////////////////////////////////////////////////// /// @notice Packs the 32-bit oracle time stamp, _oracleTimeStampU32, into the provided 256-bit slot. /// @param _slot A 256-bit container to pack the oracle time stamp within. /// @param _oracleTimeStampU32 The 32-bit oracle time stamp. /// Min. = 0, Max. = (2**32)-1. /// @return slot The modified slot containing the oracle time stamp. /// function packOracleTimeStampS2(uint256 _slot, uint256 _oracleTimeStampU32) internal pure returns (uint256 slot) { requireErrCode(_oracleTimeStampU32 <= C.MAX_U32, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ); uint256 setMask = _oracleTimeStampU32 << C.S2_OFFSET_ORACLE_TIMESTAMP; slot = (_slot & C.CLEAR_MASK_ORACLE_TIMESTAMP) | setMask; } /// @notice Unpacks the 32-bit oracle time stamp, oracleTimeStampU32, from the provided 256-bit slot, /// @param _slot A 256-bit container to unpack the oracle time stamp from. /// @return oracleTimeStampU32 The 32-bit oracle time stamp. /// Min. = 0, Max. = (2**32)-1. function unpackOracleTimeStampS2(uint256 _slot) internal pure returns (uint256 oracleTimeStampU32) { oracleTimeStampU32 = (_slot >> C.S2_OFFSET_ORACLE_TIMESTAMP) & C.MAX_U32; } // // Slot 3 Specific Packing Functions //////////////////////////////////////////////////////////////////////////////// /// @notice Packs the 3-bit fee shift, _feeShiftU3, into the provided 256-bit slot. /// @param _slot A 256-bit container to pack the fee shift into. /// @param _feeShiftU3 The 3-bit fee shift. /// Min. = 0, Max. = 7. /// @return slot The modified slot containing the new fee shift value. /// function packFeeShiftS3(uint256 _slot, uint256 _feeShiftU3) internal pure returns (uint256 slot) { requireErrCode(_feeShiftU3 <= C.MAX_U3, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ); uint256 setMask = _feeShiftU3 << C.S3_OFFSET_FEE_SHIFT_U3; slot = (_slot & C.CLEAR_MASK_FEE_SHIFT) | setMask; } /// @notice Unpacks the 3-bit fee shift, feeShiftU3, from the provided 256-bit slot, /// @param _slot A 256-bit container to unpack the fee shift from. /// @return feeShiftU3 The 3-bit fee shift. /// Min. = 0, Max. = 7. function unpackFeeShiftS3(uint256 _slot) internal pure returns (uint256 feeShiftU3) { feeShiftU3 = (_slot >> C.S3_OFFSET_FEE_SHIFT_U3) & C.MAX_U3; } // // Slot 4 Specific Packing Functions //////////////////////////////////////////////////////////////////////////////// /// @notice Packs the balancer fee, _balancerFeeDU1F18, into the provided 256-bit slot. /// @param _slot A 256-bit container to pack the balancer fee into. /// @param _balancerFeeDU1F18 The balancer fee representing a 19 decimal digit /// value with 18 fractional digits, NOT TO EXCEED 10**19. /// Min. = 0, Max. = 10**19. /// @return slot The modified slot containing the new balancer fee value. /// function packBalancerFeeS4(uint256 _slot, uint256 _balancerFeeDU1F18) internal pure returns (uint256 slot) { requireErrCode(_balancerFeeDU1F18 <= C.MAX_U60, CronErrors.VALUE_EXCEEDS_CONTAINER_SZ); uint256 setMask = _balancerFeeDU1F18 << C.S4_OFFSET_BALANCER_FEE; slot = (_slot & C.CLEAR_MASK_BALANCER_FEE) | setMask; } /// @notice Unpacks the 60-bit balancer fee representation, balancerFeeDU1F18, from the /// provided 256-bit slot, /// @param _slot A 256-bit container to unpack the balancer fee from. /// @return balancerFeeDU1F18 The 60-bit balancer fee representing a 19 decimal digit value /// with 18 fractional digits. /// Min. = 0, Max. = (2**60)-1. function unpackBalancerFeeS4(uint256 _slot) internal pure returns (uint256 balancerFeeDU1F18) { balancerFeeDU1F18 = (_slot >> C.S4_OFFSET_BALANCER_FEE) & C.MAX_U60; } }
// (c) Copyright 2023, Bad Pumpkin Inc. All Rights Reserved // // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.7.6; /// @notice Library of constants used throughout the implementation. /// /// @dev Conventions in the methods, variables and constants are as follows: /// /// Prefixes: /// /// - In constants, the prefix "Sn", where 1 <= n <= 4, denotes which slot the constant /// pertains too. There are four storage slots that are bitpacked. For example, /// "S2_OFFSET_ORACLE_TIMESTAMP" refers to the offset of the oracle timestamp in bit- /// packed storage slot 2. /// /// Suffixes: /// /// - The suffix of a variable name denotes the type contained within the variable. /// For instance "uint256 _incrementU96" is a 256-bit unsigned container representing /// the 96-bit value "_increment". /// In the case of "uint256 _balancerFeeDU1F18", the 256-bit unsigned container is /// representing a 19 digit decimal value with 18 fractional digits. In this scenario, /// the D=Decimal, U=Unsigned, F=Fractional. /// Finally, "uint128 valueU128F64" is a 128-bit container representing a 128-bit value /// with 64 fractional bits. /// /// - The suffix of a function name denotes what slot it is proprietary too as a /// matter of convention. While unchecked at run-time or by the compiler, the naming /// convention easily aids in understanding what slot a packed value is stored within. /// For instance the function "unpackFeeShiftS3" unpacks the fee shift from slot 3. If /// the value of slot 2 were passed to this method, the unpacked value would be /// incorrect. /// library C { // // Factory owner and default pool admin address //////////////////////////////////////////////////////////////////////////////// address internal constant CRON_DEPLOYER_ADMIN = 0xe122Eff60083bC550ACbf31E7d8197A58d436b39; // // General constants //////////////////////////////////////////////////////////////////////////////// address internal constant NULL_ADDR = address(0); uint256 internal constant FALSE = 0; uint256 internal constant MAX_U256 = type(uint256).max; uint256 internal constant MAX_U128 = type(uint128).max; uint256 internal constant MAX_U112 = type(uint112).max; uint256 internal constant MAX_U96 = type(uint96).max; uint256 internal constant MAX_U64 = type(uint64).max; uint256 internal constant MAX_U60 = 2**60 - 1; uint256 internal constant MAX_U32 = type(uint32).max; uint256 internal constant MAX_U24 = type(uint24).max; uint256 internal constant MAX_U20 = 0xFFFFF; uint256 internal constant MAX_U16 = type(uint16).max; uint256 internal constant MAX_U10 = 0x3FF; uint256 internal constant MAX_U8 = type(uint8).max; uint256 internal constant MAX_U3 = 0x7; uint256 internal constant MAX_U1 = 0x1; uint256 internal constant ONE_DU1_18 = 10**18; uint256 internal constant DENOMINATOR_DU1_18 = 10**18; uint256 internal constant SECONDS_PER_BLOCK = 12; // // Array Index constants //////////////////////////////////////////////////////////////////////////////// uint256 internal constant INDEX_TOKEN0 = 0; uint256 internal constant INDEX_TOKEN1 = 1; // // Bit-Packing constants // // Dev: Bit-offsets below are the offset from the first bit. For example to get // to bit 250, the offset 249 is used. (The first bit is counted as bit 1). //////////////////////////////////////////////////////////////////////////////// // Masks: uint256 internal constant CLEAR_MASK_PAIR_U96 = ~((MAX_U96 << 96) | MAX_U96); uint256 internal constant CLEAR_MASK_PAIR_U112 = ~((MAX_U112 << 112) | MAX_U112); uint256 internal constant CLEAR_MASK_ORACLE_TIMESTAMP = ~(MAX_U32 << S2_OFFSET_ORACLE_TIMESTAMP); uint256 internal constant CLEAR_MASK_FEE_SHIFT = ~(MAX_U3 << S3_OFFSET_FEE_SHIFT_U3); uint256 internal constant CLEAR_MASK_BALANCER_FEE = ~(MAX_U60 << S4_OFFSET_BALANCER_FEE); // Slot 1 Offsets: uint256 internal constant S1_OFFSET_SHORT_TERM_FEE_FP = 244; // Bits 254-245; uint256 internal constant S1_OFFSET_PARTNER_FEE_FP = 234; // Bits 244-235; uint256 internal constant S1_OFFSET_LONG_TERM_FEE_FP = 224; // Bits 234-225; // Slot 2 Offsets: uint256 internal constant S2_OFFSET_ORACLE_TIMESTAMP = 224; // Bits 256-225; // Slot 3 Offsets: uint256 internal constant S3_OFFSET_FEE_SHIFT_U3 = 222; // Bits 225-223 // Slot 4 Offsets: uint256 internal constant S4_OFFSET_PAUSE = 255; // Bit 256 uint256 internal constant S4_OFFSET_CRON_FEE_ENABLED = 254; // Bit 255 uint256 internal constant S4_OFFSET_COLLECT_BALANCER_FEES = 253; // Bit 254 uint256 internal constant S4_OFFSET_ZERO_CRONFI_FEES = 252; // Bit 253 uint256 internal constant S4_OFFSET_BALANCER_FEE = 192; // Bits 252-193; // // Scaling constants //////////////////////////////////////////////////////////////////////////////// // uint256 internal constant MAX_DECIMALS = 22; uint256 internal constant MIN_DECIMALS = 2; // // Pool Specific constants //////////////////////////////////////////////////////////////////////////////// uint256 internal constant MINIMUM_LIQUIDITY = 10**3; uint16 internal constant STABLE_OBI = 75; // ~15m @ 12s/block uint16 internal constant LIQUID_OBI = 300; // ~60m @ 12s/block uint16 internal constant VOLATILE_OBI = 1200; // ~240m @ 12s/block // Maximum long-term swap (5 years, 13149000 blocks @ 12s/block). // - Numbers below are 13149000 / OBI (rounded down where noted): uint24 internal constant STABLE_MAX_INTERVALS = 175320; uint24 internal constant LIQUID_MAX_INTERVALS = 43830; uint24 internal constant VOLATILE_MAX_INTERVALS = 10957; // Rounded down from 10957.5 // // Fees constants //////////////////////////////////////////////////////////////////////////////// // FP = Total Fee Points // ST = Short-Term Swap // LT = Long-Term Swap // LP = Liquidity Provider // CF = Cron Fi // // NOTE: Mult-by these constants requires Max. 14-bits (~13.3 bits) headroom to prevent overflow. // uint256 internal constant TOTAL_FP = 100000; uint256 internal constant MAX_FEE_FP = 1000; // 1.000% // Short Term Swap Payouts: // ---------------------------------------- uint16 internal constant STABLE_ST_FEE_FP = 10; // 0.010% uint16 internal constant LIQUID_ST_FEE_FP = 50; // 0.050% uint16 internal constant VOLATILE_ST_FEE_FP = 100; // 0.100% // Partner Swap Payouts: // ---------------------------------------- uint16 internal constant STABLE_ST_PARTNER_FEE_FP = 5; // 0.005% uint16 internal constant LIQUID_ST_PARTNER_FEE_FP = 25; // 0.025% uint16 internal constant VOLATILE_ST_PARTNER_FEE_FP = 50; // 0.050% // Long Term Swap Payouts // ---------------------------------------- uint16 internal constant STABLE_LT_FEE_FP = 30; // 0.030% uint16 internal constant LIQUID_LT_FEE_FP = 150; // 0.150% uint16 internal constant VOLATILE_LT_FEE_FP = 300; // 0.300% uint8 internal constant DEFAULT_FEE_SHIFT = 1; // 66% LP to 33% CronFi }
// SPDX-License-Identifier: GPL-3.0-or-later // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // NOTE: Adapted from Balancer's BalancerErrors.sol code. pragma solidity ^0.7.6; /// @dev Conventions in the methods below are as follows: /// /// Suffixes: /// /// - The suffix of a variable name denotes the type contained within the variable. /// For instance "uint256 _incrementU96" is a 256-bit unsigned container representing /// a 96-bit value, _increment. /// In the case of "uint256 _balancerFeeDU1F18", the 256-bit unsigned container is /// representing a 19 digit decimal value with 18 fractional digits. In this scenario, /// the D=Decimal, U=Unsigned, F=Fractional. /// /// - The suffix of a function name denotes what slot it is proprietary too as a /// matter of convention. While unchecked at run-time or by the compiler, the naming /// convention easily aids in understanding what slot a packed value is stored within. /// For instance the function "unpackFeeShiftS3" unpacks the fee shift from slot 3. If /// the value of slot 2 were passed to this method, the unpacked value would be /// incorrect. /// @notice Reverts if the specified condition is not true with the provided error code. /// @param _condition A condition to test; must resolve to true to not revert. /// @param _errorCodeD3 An 3 digit decimal error code to present if the condition /// resolves to false. /// Min. = 0, Max. = 999. /// @dev WARNING: No checks of _errorCodeD3 are performed for efficiency! /// // solhint-disable-next-line func-visibility function requireErrCode(bool _condition, uint256 _errorCodeD3) pure { if (!_condition) { // We're going to dynamically create a revert string based on the error code, with the following format: // 'CFI#{errorCode}' // where the code is left-padded with zeroes to three digits (so they range from 000 to 999). // // We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a // number (8 to 16 bits) than the individual string characters. // // The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a // much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a // safe place to rely on it without worrying about how its usage might affect e.g. memory contents. // solhint-disable-next-line no-inline-assembly assembly { // First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999 // range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for // the '0' character. let units := add(mod(_errorCodeD3, 10), 0x30) _errorCodeD3 := div(_errorCodeD3, 10) let tenths := add(mod(_errorCodeD3, 10), 0x30) _errorCodeD3 := div(_errorCodeD3, 10) let hundreds := add(mod(_errorCodeD3, 10), 0x30) // With the individual characters, we can now construct the full string. The "CFI#" part is a known constant // (0x43464923): we simply shift this by 24 (to provide space for the 3 bytes of the error code), and add the // characters to it, each shifted by a multiple of 8. // The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits // per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte // array). let revertReason := shl(200, add(0x43464923000000, add(add(units, shl(8, tenths)), shl(16, hundreds)))) // We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded // message will have the following layout: // [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ] // The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We // also write zeroes to the next 28 bytes of memory, but those are about to be overwritten. mstore(0x0, 0x08c379a000000000000000000000000000000000000000000000000000000000) // Next is the offset to the location of the string, which will be placed immediately after (20 bytes away). mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020) // The string length is fixed: 7 characters. mstore(0x24, 7) // Finally, the string itself is stored. mstore(0x44, revertReason) // Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of // the encoded message is therefore 4 + 32 + 32 + 32 = 100. revert(0, 100) } } } library CronErrors { // // Permissions //////////////////////////////////////////////////////////////////////////////// uint256 internal constant SENDER_NOT_FACTORY = 0; uint256 internal constant SENDER_NOT_FACTORY_OWNER = 1; uint256 internal constant SENDER_NOT_ADMIN = 2; uint256 internal constant SENDER_NOT_ARBITRAGE_PARTNER = 3; uint256 internal constant NON_VAULT_CALLER = 4; uint256 internal constant SENDER_NOT_PARTNER = 5; uint256 internal constant SENDER_NOT_FEE_ADDRESS = 7; uint256 internal constant SENDER_NOT_ORDER_OWNER_OR_DELEGATE = 8; uint256 internal constant CANNOT_TRANSFER_TO_SELF_OR_NULL = 9; uint256 internal constant RECIPIENT_NOT_OWNER = 10; // A cleared order can be one that: // - was cancelled // - was withdrawn after expiry // - never existed (i.e. empty blockchain state in the future) uint256 internal constant CLEARED_ORDER = 11; // // Modifiers //////////////////////////////////////////////////////////////////////////////// uint256 internal constant POOL_PAUSED = 100; // // Configuration & Parameterization //////////////////////////////////////////////////////////////////////////////// uint256 internal constant UNSUPPORTED_SWAP_KIND = 201; uint256 internal constant INSUFFICIENT_LIQUIDITY = 204; uint256 internal constant INCORRECT_POOL_ID = 206; uint256 internal constant ZERO_SALES_RATE = 208; uint256 internal constant NO_FUNDS_AVAILABLE = 212; uint256 internal constant MAX_ORDER_LENGTH_EXCEEDED = 223; uint256 internal constant NO_FEES_AVAILABLE = 224; uint256 internal constant UNSUPPORTED_TOKEN_DECIMALS = 225; uint256 internal constant NULL_RECIPIENT_ON_JOIN = 226; uint256 internal constant CANT_CANCEL_COMPLETED_ORDER = 227; uint256 internal constant MINIMUM_NOT_SATISFIED = 228; // // General //////////////////////////////////////////////////////////////////////////////// uint256 internal constant VALUE_EXCEEDS_CONTAINER_SZ = 400; uint256 internal constant OVERFLOW = 401; uint256 internal constant UNDERFLOW = 402; uint256 internal constant PARAM_ERROR = 403; // // Factory //////////////////////////////////////////////////////////////////////////////// uint256 internal constant ZERO_TOKEN_ADDRESSES = 500; uint256 internal constant IDENTICAL_TOKEN_ADDRESSES = 501; uint256 internal constant EXISTING_POOL = 502; uint256 internal constant INVALID_FACTORY_OWNER = 503; uint256 internal constant INVALID_PENDING_OWNER = 504; uint256 internal constant NON_EXISTING_POOL = 505; // // Periphery Relayer //////////////////////////////////////////////////////////////////////////////// uint256 internal constant P_ETH_TRANSFER = 600; uint256 internal constant P_NULL_USER_ADDRESS = 602; uint256 internal constant P_INSUFFICIENT_LIQUIDITY = 603; uint256 internal constant P_INSUFFICIENT_TOKEN_A_USER_BALANCE = 604; uint256 internal constant P_INSUFFICIENT_TOKEN_B_USER_BALANCE = 605; uint256 internal constant P_INVALID_POOL_TOKEN_AMOUNT = 606; uint256 internal constant P_INSUFFICIENT_POOL_TOKEN_USER_BALANCE = 607; uint256 internal constant P_INVALID_INTERVAL_AMOUNT = 608; uint256 internal constant P_DELEGATE_WITHDRAW_RECIPIENT_NOT_OWNER = 609; uint256 internal constant P_INVALID_OR_EXPIRED_ORDER_ID = 610; uint256 internal constant P_WITHDRAW_BY_ORDER_OR_DELEGATE_ONLY = 611; uint256 internal constant P_DELEGATE_CANCEL_RECIPIENT_NOT_OWNER = 612; uint256 internal constant P_CANCEL_BY_ORDER_OR_DELEGATE_ONLY = 613; uint256 internal constant P_INVALID_TOKEN_IN_ADDRESS = 614; uint256 internal constant P_INVALID_TOKEN_OUT_ADDRESS = 615; uint256 internal constant P_INVALID_POOL_TYPE = 616; uint256 internal constant P_NON_EXISTING_POOL = 617; uint256 internal constant P_INVALID_POOL_ADDRESS = 618; uint256 internal constant P_INVALID_AMOUNT_IN = 619; uint256 internal constant P_INSUFFICIENT_TOKEN_IN_USER_BALANCE = 620; uint256 internal constant P_POOL_HAS_NO_LIQUIDITY = 621; uint256 internal constant P_MAX_ORDER_LENGTH_EXCEEDED = 622; uint256 internal constant P_NOT_IMPLEMENTED = 624; uint256 internal constant P_MULTICALL_NOT_SUPPORTED = 625; }
// (c) Copyright 2022, Bad Pumpkin Inc. All Rights Reserved // // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.7.6; /// @notice Square-root function for providing initial liquidity. /// @dev Sourced from Uniswap V2 library: /// - https://github.com/Uniswap/v2-core/blob/master/contracts/libraries/Math.sol /// @dev Based on Babylonian Method: /// - https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method /// @param _value Is a number to approximate the square root of. /// @return root The approximate square root of _value using the Babylonian Method. /// // solhint-disable-next-line func-visibility function sqrt(uint256 _value) pure returns (uint256 root) { if (_value > 3) { root = _value; uint256 iteration = _value / 2 + 1; while (iteration < root) { root = iteration; iteration = (_value / iteration + iteration) / 2; } } else if (_value != 0) { root = 1; } }
{ "remappings": [ "@balancer-labs/=node_modules/@balancer-labs/", "@openzeppelin/=node_modules/@openzeppelin/", "@rari-capital/=node_modules/@rari-capital/", "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "hardhat/=node_modules/hardhat/", "prb-math/=node_modules/prb-math/" ], "optimizer": { "enabled": true, "runs": 575 }, "metadata": { "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "istanbul", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract IVault","name":"_vault","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":true,"internalType":"address","name":"token0","type":"address"},{"indexed":true,"internalType":"address","name":"token1","type":"address"},{"indexed":false,"internalType":"enum ICronV1PoolEnums.PoolType","name":"poolType","type":"uint8"}],"name":"CronV1PoolCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":true,"internalType":"address","name":"token0","type":"address"},{"indexed":true,"internalType":"address","name":"token1","type":"address"},{"indexed":false,"internalType":"enum ICronV1PoolEnums.PoolType","name":"poolType","type":"uint8"}],"name":"CronV1PoolRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":true,"internalType":"address","name":"token0","type":"address"},{"indexed":true,"internalType":"address","name":"token1","type":"address"},{"indexed":false,"internalType":"enum ICronV1PoolEnums.PoolType","name":"poolType","type":"uint8"}],"name":"CronV1PoolSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newAdmin","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"}],"name":"PoolCreated","type":"event"},{"inputs":[],"name":"claimOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token0","type":"address"},{"internalType":"address","name":"_token1","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint256","name":"_poolType","type":"uint256"}],"name":"create","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token0","type":"address"},{"internalType":"address","name":"_token1","type":"address"},{"internalType":"uint256","name":"_poolType","type":"uint256"}],"name":"getPool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVault","outputs":[{"internalType":"contract IVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"}],"name":"isPoolFromFactory","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token0","type":"address"},{"internalType":"address","name":"_token1","type":"address"},{"internalType":"uint256","name":"_poolType","type":"uint256"}],"name":"remove","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token0","type":"address"},{"internalType":"address","name":"_token1","type":"address"},{"internalType":"uint256","name":"_poolType","type":"uint256"},{"internalType":"address","name":"_pool","type":"address"}],"name":"set","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"},{"internalType":"bool","name":"_direct","type":"bool"},{"internalType":"bool","name":"_renounce","type":"bool"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60a060405234801561001057600080fd5b506040516160773803806160778339818101604052602081101561003357600080fd5b50516001600160601b0319606082901b16608052600180546001600160a01b0319163317908190556040516001600160a01b0391909116906000907fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c908290a35060805160601c615fc66100b16000398061060f5250615fc66000f3fe60806040523480156200001157600080fd5b5060043610620000d05760003560e01c80638da5cb5b1162000081578063a53253431162000063578063a532534314620001ff578063acf6cc1a1462000350578063e30c3978146200038957620000d0565b80638da5cb5b14620001b65780638f62b25c14620001c057620000d0565b806359e5fd0411620000b757806359e5fd04146200011a5780636634b75314620001535780638d928af8146200019057620000d0565b8063078dfbe714620000d55780634e71e0c81462000110575b600080fd5b6200010e60048036036060811015620000ed57600080fd5b506001600160a01b0381351690602081013515159060400135151562000393565b005b6200010e62000460565b6200010e600480360360608110156200013257600080fd5b506001600160a01b03813581169160208101359091169060400135620004df565b6200017c600480360360208110156200016b57600080fd5b50356001600160a01b0316620005ef565b604080519115158252519081900360200190f35b6200019a6200060d565b604080516001600160a01b039092168252519081900360200190f35b6200019a62000631565b6200010e60048036036080811015620001d857600080fd5b506001600160a01b0381358116916020810135821691604082013591606001351662000640565b6200019a600480360360a08110156200021757600080fd5b6001600160a01b0382358116926020810135909116918101906060810160408201356401000000008111156200024c57600080fd5b8201836020820111156200025f57600080fd5b803590602001918460018302840111640100000000831117156200028257600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050640100000000811115620002d657600080fd5b820183602082011115620002e957600080fd5b803590602001918460018302840111640100000000831117156200030c57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550509135925062000700915050565b6200019a600480360360608110156200036857600080fd5b506001600160a01b03813581169160208101359091169060400135620009bd565b6200019a62000a21565b600154620003af906001600160a01b031633146101f762000a30565b81156200043f57620003d86001600160a01b038416151580620003cf5750815b6101f462000a30565b6001546040516001600160a01b038086169216907fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c90600090a3600180546001600160a01b0385166001600160a01b0319918216179091556002805490911690556200045b565b600280546001600160a01b0319166001600160a01b0385161790555b505050565b6002546001600160a01b03166200047c3382146101f862000a30565b6001546040516001600160a01b038084169216907fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c90600090a3600180546001600160a01b039092166001600160a01b0319928316179055600280549091169055565b600154620004fb906001600160a01b031633146101f762000a30565b6001600160a01b03808416600090815260036020908152604080832086851684528252808320858452909152902054166200053b8115156101f962000a30565b6001600160a01b0380851660009081526003602090815260408083209387168352928152828220858352905290812080546001600160a01b03191690558260028111156200058557fe5b9050836001600160a01b0316856001600160a01b0316836001600160a01b03167fec6e3eb5f34b5df1c942ddf42cdd59bd65528ffad1dc521e7b51b0f96eb0e4338460405180826002811115620005d857fe5b815260200191505060405180910390a45050505050565b6001600160a01b031660009081526020819052604090205460ff1690565b7f000000000000000000000000000000000000000000000000000000000000000090565b6001546001600160a01b031681565b6001546200065c906001600160a01b031633146101f762000a30565b6001600160a01b03848116600090815260036020908152604080832087851684528252808320868452909152812080546001600160a01b03191692841692909217909155826002811115620006ad57fe5b9050836001600160a01b0316856001600160a01b0316836001600160a01b03167fd9bdd19c2f64837c4d11b9979d041e4f60cf82f7e3b68a9f80f919ebfe8986c68460405180826002811115620005d857fe5b6000808260028111156200071057fe5b905062000735866001600160a01b0316886001600160a01b031614156101f562000a30565b600080876001600160a01b0316896001600160a01b0316106200075a5787896200075d565b88885b90925090506200077b6001600160a01b03831615156101f462000a30565b6001600160a01b03828116600090815260036020908152604080832085851684528252808320898452909152902054620007ba9116156101f662000a30565b60008282620007c86200060d565b8a8a88604051620007d99062000ad9565b80876001600160a01b03168152602001866001600160a01b03168152602001856001600160a01b0316815260200180602001806020018460028111156200081c57fe5b8152602001838103835286818151815260200191508051906020019080838360005b83811015620008585781810151838201526020016200083e565b50505050905090810190601f168015620008865780820380516001836020036101000a031916815260200191505b50838103825285518152855160209182019187019080838360005b83811015620008bb578181015183820152602001620008a1565b50505050905090810190601f168015620008e95780820380516001836020036101000a031916815260200191505b5098505050505050505050604051809103906000f08015801562000911573d6000803e3d6000fd5b5090506200091f8162000a8d565b6001600160a01b038381166000908152600360209081526040808320868516845282528083208a84529091529081902080546001600160a01b03191684841690811790915590518b8316928d1691907faeff84bd0403e2418c457955d4258f28133c5b304b671114fc854725bb098bee90889080826002811115620009a057fe5b815260200191505060405180910390a49998505050505050505050565b6000806000846001600160a01b0316866001600160a01b031610620009e4578486620009e7565b85855b6001600160a01b03918216600090815260036020908152604080832093851683529281528282208883529052205416925050509392505050565b6002546001600160a01b031681565b8162000a895762461bcd60e51b6000908152602060045260076024526643464923000030600a808404818106603090810160081b95839006959095019082900491820690940160101b939093010160c81b604452606490fd5b5050565b6001600160a01b038116600081815260208190526040808220805460ff19166001179055517f83a48fbcfc991335314e74d0496aab6a1987e992ddc85dddbcc4d6dd6ef2e9fc9190a250565b6154a98062000ae88339019056fe6102406040527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c960e0523480156200003657600080fd5b50604051620054a9380380620054a98339810160408190526200005991620009a4565b6040805180820190915260018152603160f81b602080830191825285519086019081206080529151902060a0527f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60c052835184918491620000be9160039162000844565b508051620000d490600490602084019062000844565b50506001600655503360601b610220526040516309b2760f60e01b81526000906001600160a01b038616906309b2760f90620001169060029060040162000b32565b602060405180830381600087803b1580156200013157600080fd5b505af115801562000146573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200016c91906200098b565b60408051600280825260608201835292935060009290916020830190803683370190505090508781600081518110620001a157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508681600181518110620001d057fe5b6001600160a01b03928316602091820292909201015260408051600280825260608201909252918816916366a9c7d29185918591816020016020820280368337019050506040518463ffffffff1660e01b8152600401620002349392919062000a98565b600060405180830381600087803b1580156200024f57600080fd5b505af115801562000264573d6000803e3d6000fd5b5050506001600160601b0319606088811b8216610100526101208590528a811b82166101405289901b166101605250826002811115620002a057fe5b610180816002811115620002b057fe5b60f81b815250506000886001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015620002f357600080fd5b505afa15801562000308573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200032e919062000a63565b60ff16905062000352816002111580156200034a575060168211155b60e162000766565b80600101600a0a6101e081815250506000886001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156200039d57600080fd5b505afa158015620003b2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003d8919062000a63565b60ff169050620003fb816002111580156200034a5750601682111560e162000766565b60018101600a0a6102005260008560028111156200041557fe5b14620004435760018560028111156200042a57fe5b1462000439576104b06200043d565b61012c5b62000446565b604b5b60f01b6001600160f01b0319166101a05260008560028111156200046657fe5b14620004945760018560028111156200047b57fe5b146200048a57612acd6200048e565b61ab365b62000499565b6202acd85b60e81b6001600160e81b0319166101c0526000856002811115620004b957fe5b14156200052b576000620004e16000600a61ffff1660f4620007c360201b62001b241760201c565b90506200050181600561ffff1660ea620007c360201b62001b241760201c565b90506200052181601e61ffff1660e0620007c360201b62001b241760201c565b600e555062000609565b60018560028111156200053a57fe5b1415620005a2576000620005626000603261ffff1660f4620007c360201b62001b241760201c565b90506200058281601961ffff1660ea620007c360201b62001b241760201c565b90506200052181609661ffff1660e0620007c360201b62001b241760201c565b6000620005c36000606461ffff1660f4620007c360201b62001b241760201c565b9050620005e381603261ffff1660ea620007c360201b62001b241760201c565b9050620006048161012c61ffff1660e0620007c360201b62001b241760201c565b600e55505b62000626601054600160ff16620007e960201b62001b481760201c565b60108190555060006200064860008060ff6200080f60201b62001b6c1760201c565b90506200066481600060fe6200080f60201b62001b6c1760201c565b90506200068081600160fd6200080f60201b62001b6c1760201c565b90506200069c81600160fc6200080f60201b62001b6c1760201c565b60115573e122eff60083bc550acbf31e7d8197a58d436b39600081905260146020527f65af30822488a24bf4917ce195d1b124a7dbfde2fa518b38c26d567c783e216e805460ff1916600190811790915560405133917fe8b9bbf829bebf34c09fc3a715a8e78509552d2d567e7682e24229f1c16693059162000720919062000a8d565b60405180910390a360405160009033907fb5c9ea0e9f459f0f9f5e75158bb0d1e656d758ede76198f5843b070d1676e517908390a3505050505050505050505062000b60565b81620007bf5762461bcd60e51b6000908152602060045260076024526643464923000030600a808404818106603090810160081b95839006959095019082900491820690940160101b939093010160c81b604452606490fd5b5050565b6000620007d86103ff84111561019062000766565b506103ff811b1992909216911b1790565b6000620007fd600783111561019062000766565b5060de1b600760de1b19919091161790565b600062000823600184111561019062000766565b6001821b836200083757801985166200083b565b8085175b95945050505050565b828054600181600116156101000203166002900490600052602060002090601f0160209004810192826200087c5760008555620008c7565b82601f106200089757805160ff1916838001178555620008c7565b82800160010185558215620008c7579182015b82811115620008c7578251825591602001919060010190620008aa565b50620008d5929150620008d9565b5090565b5b80821115620008d55760008155600101620008da565b600082601f83011262000901578081fd5b81516001600160401b03808211156200091657fe5b6040516020601f8401601f19168201810183811183821017156200093657fe5b60405283825285840181018710156200094d578485fd5b8492505b8383101562000970578583018101518284018201529182019162000951565b838311156200098157848185840101525b5095945050505050565b6000602082840312156200099d578081fd5b5051919050565b60008060008060008060c08789031215620009bd578182fd5b8651620009ca8162000b47565b6020880151909650620009dd8162000b47565b6040880151909550620009f08162000b47565b60608801519094506001600160401b038082111562000a0d578384fd5b62000a1b8a838b01620008f0565b9450608089015191508082111562000a31578384fd5b5062000a4089828a01620008f0565b92505060a08701516003811062000a55578182fd5b809150509295509295509295565b60006020828403121562000a75578081fd5b815160ff8116811462000a86578182fd5b9392505050565b901515815260200190565b60006060820185835260206060818501528186518084526080860191508288019350845b8181101562000ae35784516001600160a01b03168352938301939183019160010162000abc565b505084810360408601528551808252908201925081860190845b8181101562000b245782516001600160a01b03168552938301939183019160010162000afd565b509298975050505050505050565b602081016003831062000b4157fe5b91905290565b6001600160a01b038116811462000b5d57600080fd5b50565b60805160a05160c05160e0516101005160601c610120516101405160601c6101605160601c6101805160f81c6101a05160f01c6101c05160e81c6101e051610200516102205160601c61484a62000c5f60003980611f0e525080613a005280613c99525080613a265280613c4e52508061266952508061269b52806126d25280612e1d5280612f4f528061370a525080610c90525080612460528061249c5250806110e2528061243a52806124c25250806119675280611a035280611d1a528061212752508061105d52806119d65280611ced52806120e85280612c4652508061155f525080611ffe52508061204052508061201f525061484a6000f3fe608060405234801561001057600080fd5b506004361061032b5760003560e01c80638705fcd4116101b2578063d505accf116100f9578063e0b20d6c116100a2578063ee7293561161007c578063ee72935614610658578063f449dbc814610660578063fa6c4b4f14610668578063fca513a81461067b5761032b565b8063e0b20d6c14610640578063e0d7d0e914610648578063e84b1f3f146106505761032b565b8063dd62ed3e116100d3578063dd62ed3e14610612578063ddba751414610625578063df0f11af1461062d5761032b565b8063d505accf146105d9578063d5c096c4146105ec578063d73dd623146105ff5761032b565b8063b187bd261161015b578063bedb86fb11610135578063bedb86fb14610584578063bf37843c14610597578063d09ef241146105b95761032b565b8063b187bd261461056c578063b4456b3614610574578063bc36419f1461057c5761032b565b8063a511eb7c1161018c578063a511eb7c1461053e578063a6c449c114610546578063a9059cbb146105595761032b565b80638705fcd41461051057806395d89b41146105235780639d2c110c1461052b5761032b565b80633644e5151161027657806374f3b0091161021f5780637ca2c357116101f95780637ca2c357146104ed5780637ecebe00146104f55780637f6de94b146105085761032b565b806374f3b009146104b157806377f53c2f146104d257806378b45070146104da5761032b565b8063648da85911610250578063648da85914610476578063661884631461048b57806370a082311461049e5761032b565b80633644e515146104315780634f5bee44146104395780634ff643211461044c5761032b565b806323b872dd116102d857806330c55ddb116102b257806330c55ddb146103ff57806330c5ee4614610407578063313ce5671461041c5761032b565b806323b872dd146103c357806327ca08a2146103d65780633023f7c8146103e95761032b565b806318160ddd1161030957806318160ddd146103835780631822a4211461039857806319ebefa7146103a05761032b565b806306fdde0314610330578063095ea7b31461034e57806312ee63f31461036e575b600080fd5b610338610692565b604051610345919061462c565b60405180910390f35b61036161035c366004614082565b610729565b60405161034591906145d1565b61038161037c366004614455565b610740565b005b61038b610854565b60405161034591906145dc565b61038b61085a565b6103b36103ae3660046143af565b61086e565b604051610345949392919061472e565b6103616103d1366004613fa0565b6108de565b6103816103e43660046141ad565b61095f565b6103f16109d9565b6040516103459291906146e6565b6103616109f0565b61040f610a07565b60405161034591906144d3565b610424610b22565b6040516103459190614797565b61038b610b27565b610381610447366004614055565b610b31565b61045f61045a366004614431565b610ba7565b6040516103459b9a99989796959493929190614749565b61047e610c8e565b6040516103459190614619565b610361610499366004614082565b610cb2565b61038b6104ac366004613f30565b610d0c565b6104c46104bf3660046141e5565b610d2b565b604051610345929190614587565b61038b610ea4565b6103816104e83660046143af565b610eaa565b61038b610f18565b61038b610503366004613f30565b610f27565b6103f1610f42565b61038161051e366004613f30565b610f50565b610338610fe4565b61038b6105393660046142b8565b611045565b6103f16112fd565b6103f16105543660046143af565b611315565b610361610567366004614082565b611339565b610361611346565b61038b611356565b6103f1611363565b6103816105923660046141ad565b611371565b6105aa6105a53660046140ad565b6113d8565b604051610345939291906145ac565b6105cc6105c73660046143af565b6114a8565b604051610345919061467f565b6103816105e7366004613fe0565b611530565b6104c46105fa3660046141e5565b6116a0565b61036161060d366004614082565b611864565b61038b610620366004613f68565b61189a565b6103616118c5565b61038161063b366004613f68565b6118d5565b61038b611958565b61038b611965565b61038b611989565b6103f161198f565b61038b61199d565b6103816106763660046143af565b6119ac565b610683611b02565b604051610345939291906146f4565b60038054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561071e5780601f106106f35761010080835404028352916020019161071e565b820191906000526020600020905b81548152906001019060200180831161070157829003601f168201915b505050505090505b90565b6000610736338484611b9d565b5060015b92915050565b610748611bff565b610750611c1f565b600082600281111561075e57fe5b905060008082600281111561076f57fe5b141561077d575060f46107b1565b600182600281111561078b57fe5b1415610799575060ea6107b1565b60028260028111156107a757fe5b14156107b1575060e05b8260028360028111156107c057fe5b111580156107cd57508115155b80156107db57506103e88411155b156107f5576107ed600e548284611b24565b600e55610802565b6108026000610193611c38565b336001600160a01b03167f225ac322492a1d8feb500fcd29c839e1e846186ea09a2f37d3bb8974f4e85f45848360405161083d929190614602565b60405180910390a2505050610850611c90565b5050565b60025490565b6000610869600e5460e0611c97565b905090565b60008060008061087c613ce1565b610887866000611c9f565b600c54909350909150156108d65760006108a083611de5565b60125460135463ffffffff928316995090975095509091508116156108d457816101a0015185019450816101c00151840193505b505b509193509193565b6001600160a01b0383166000818152600160209081526040808320338085529252822054919261091c9114806109145750838210155b610197611e31565b610927858585611e3f565b336001600160a01b0386161480159061094257506000198114155b15610954576109548533858403611b9d565b506001949350505050565b610967611f09565b61096f611c1f565b61098e60115482610981576000610984565b60015b60ff1660fd611b6c565b60115560405133907f9d7e9829e0e608d50be70896ea553fe5ba9db33c11a836495412b8b8481aa4dd906109c69060019085906145e5565b60405180910390a26109d6611c90565b50565b6000806109e7600f54611fb8565b90939092509050565b600080610a0060115460fd611fcd565b1415905090565b6000610a11611fd4565b610a19611c1f565b336000908152601560205260408120546001600160a01b0316805b809250826001600160a01b03166355fcff3c6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015610a7257600080fd5b505af1158015610a86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aaa9190613f4c565b90506001600160a01b038116610a34573360008181526015602052604080822080546001600160a01b0319166001600160a01b03888116918217909255915191939086169290917fe0e07986073e388ace1090c16866621627f1f1299f7aa008589d4d45b61034ab9190a45090915050610726611c90565b601290565b6000610869611ffa565b610b39611f09565b610b41611c1f565b6001600160a01b03821660008181526014602052604090819020805460ff19168415151790555133907fe8b9bbf829bebf34c09fc3a715a8e78509552d2d567e7682e24229f1c166930590610b979085906145d1565b60405180910390a3610850611c90565b6000806000806000806000806000806000610bc0613ce1565b610bca8e8e611c9f565b809d50819250505080600001519a5080602001519950600080610bee600e54611fb8565b9150915082610120015182039a5082610140015181039950600080610c14600f54611fb8565b9150915084610160015182019a5084610180015181019950600080610c3a6011546120b8565b915091508660a0015182019a508660c0015181019950600080610c5e6010546120b8565b915091508860e0015182019a50886101000151810199505050505050505050509295989b509295989b9093969950565b7f000000000000000000000000000000000000000000000000000000000000000081565b3360009081526001602090815260408083206001600160a01b0386168452909152812054808310610cee57610ce933856000611b9d565b610d02565b610d023385610cfd84876120cd565b611b9d565b5060019392505050565b6001600160a01b0381166000908152602081905260409020545b919050565b606080610d38338b6120e3565b60008080610d4886880188614455565b92509050806003811115610d5857fe5b925060009050806003846003811115610d6d57fe5b1415610d8657610d7c8d61214f565b9092509050610e34565b600080610dbc8d600081518110610d9957fe5b60200260200101518e600181518110610dae57fe5b6020026020010151436121fb565b90925090506000866003811115610dcf57fe5b1415610de757610de18f8684846122e3565b90945092505b5060019050846003811115610df857fe5b1480610e0f57506002846003811115610e0d57fe5b145b15610e3457610e2e838e8e6002886003811115610e2857fe5b14612371565b90925090505b60408051600280825260608201835290916020830190803683370190505095508186600081518110610e6257fe5b6020026020010181815250508086600181518110610e7c57fe5b602002602001018181525050610e9189612574565b9450505050509850989650505050505050565b600c5490565b610eb2611f09565b610eba611c1f565b610ed760018210158015610ecf575060048211155b610193611c38565b610ee360105482611b48565b60105560405133907f5acdc353c39f707c917be1fcdfd9f0ef222602d718c114688a00abd2fc80e8fd906109c69084906145dc565b6000610869600e5460f4611c97565b6001600160a01b031660009081526005602052604090205490565b6000806109e76010546120b8565b610f58611f09565b610f60611c1f565b601680546001600160a01b0319166001600160a01b038316908117909155600090610f8c576000610f8f565b60015b60ff169050610fa26011548260fe611b6c565b6011556040516001600160a01b0383169033907fb5c9ea0e9f459f0f9f5e75158bb0d1e656d758ede76198f5843b070d1676e51790600090a3506109d6611c90565b60048054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561071e5780601f106106f35761010080835404028352916020019161071e565b600061104f61264f565b611085336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146004611c38565b602084015160c085015160608601516110ae6000885160018111156110a657fe5b1460c9611c38565b6000808861010001518060200190518101906110ca9190614476565b9150915060008260028111156110dc57fe5b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316876001600160a01b031614905060008061113d83611128578b61112a565b8c5b84611135578d611137565b8c5b436121fb565b9092509050600184600281111561115057fe5b14156111cc5760e08d0151600061116a8a83878c8b612662565b90508a6001600160a01b0316826001600160a01b03168b6001600160a01b03167f5aba2dae7ef407628bfbaf988767303f2c5ac508d40c737736efe9536c52fbeb8c8b866040516111bd939291906146f4565b60405180910390a450506112ed565b60028460028111156111da57fe5b1415611280576001600160a01b038086166000908152601560205260409081902054905163387f517760e01b815291169061127e90829063387f517790611225908d906004016144d3565b602060405180830381600087803b15801561123f57600080fd5b505af1158015611253573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061127791906141c9565b6005611c38565b505b61129b83600086600281111561129257fe5b1489858561292c565b9950886001600160a01b0316886001600160a01b03167f37e41badd72aca9bedfbfa5f3c99a2b3b408e7b966e604063a37af67cd081d10898d8a6040516112e4939291906146f4565b60405180910390a35b5050505050505050509392505050565b6007546001600160701b03607082901c811692911690565b600081815260096020526040812054819061132f90611fb8565b9094909350915050565b6000610736338484611e3f565b600080610a0060115460ff611fcd565b6000610869601154612a06565b6000806109e7600e54611fb8565b611379611bff565b611381611c1f565b6113a060115482611393576000611396565b60015b60ff1660ff611b6c565b60115560405133907f9d7e9829e0e608d50be70896ea553fe5ba9db33c11a836495412b8b8481aa4dd906109c69060009085906145e5565b6001600160a01b03831660009081526017602052604081208054606092918285156114035785611405565b825b90508067ffffffffffffffff8111801561141e57600080fd5b50604051908082528060200260200182016040528015611448578160200160208202803683370190505b509450865b818510801561145b57508381105b1561149c5782818154811061146c57fe5b906000526020600020015486868060010197508151811061148957fe5b602090810291909101015260010161144d565b50505093509350939050565b6114b0613d57565b506000908152600b6020908152604091829020825160c081018452815460ff81161515825261010081046001600160701b031693820193909352600160781b9092046001600160801b03169282019290925260018201546001600160a01b039081166060830152600283015416608082015260039091015460a082015290565b61153e8442111560d1611e31565b6001600160a01b0380881660008181526005602090815260408083205481517f00000000000000000000000000000000000000000000000000000000000000008185015280830195909552948b166060850152608084018a905260a0840185905260c08085018a90528151808603909101815260e090940190528251920191909120906115ca82612a16565b9050600060018288888860405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611628573d6000803e3d6000fd5b5050604051601f190151915061166a90506001600160a01b0382161580159061166257508b6001600160a01b0316826001600160a01b0316145b6101f8611e31565b6001600160a01b038b1660009081526005602052604090206001850190556116938b8b8b611b9d565b5050505050505050505050565b6060806116ad338b6120e3565b60006060816116be868801886143c7565b909650925090508060018111156116d157fe5b9250506000846000815181106116e357fe5b602002602001015190506000856001815181106116fc57fe5b602002602001015190506000611710611346565b6117de576000808087600181111561172457fe5b1480156117375750611734610854565b15155b8061174d5750600187600181111561174b57fe5b145b1561177d576117778e60008151811061176257fe5b60200260200101518f600181518110610dae57fe5b90925090505b600087600181111561178b57fe5b14156117d7576117c78585886000815181106117a357fe5b6020026020010151896001815181106117b857fe5b60200260200101518686612a62565b6117d48f86868585612abc565b92505b50506117f8565b6117f860008660018111156117ef57fe5b14156064611c38565b8c6001600160a01b03168e6001600160a01b03167f54f5163abddef0c7b641204242ef436658d731009e7630bd815e3d52383b0c7085858560405161183f939291906146f4565b60405180910390a36118508a612574565b955050505050509850989650505050505050565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091610736918590610cfd9086612b80565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600080610a0060115460fe611fcd565b6118dd611bff565b6118e5611c1f565b6001600160a01b038281166000908152601560205260409081902080546001600160a01b031916928416929092179091555133907f14298ee83383fc0fa7913c34ea36b3209010c03d0a5a09c606462f80a5f25e589061194890859085906144e7565b60405180910390a2610850611c90565b6000610869601054612b99565b7f000000000000000000000000000000000000000000000000000000000000000081565b600d5490565b6000806109e76011546120b8565b6000610869600e5460ea611c97565b6119b4611c1f565b6119bc612ba2565b604051631f29a8cd60e31b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063f94d466890611a2b907f0000000000000000000000000000000000000000000000000000000000000000906004016145dc565b60006040518083038186803b158015611a4357600080fd5b505afa158015611a57573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a7f91908101906140e1565b50915050611ab681600081518110611a9357fe5b602002602001015182600181518110611aa857fe5b6020026020010151846121fb565b5050336001600160a01b03167f9b6574d1f0a54e6f2d8510751cd8764d42de16aa67970a9bc544cb1028bcdb2683604051611af191906145dc565b60405180910390a2506109d6611c90565b6000806000611b12600f54612cb2565b60125460135491959094509092509050565b6000611b376103ff841115610190611c38565b506103ff811b1992909216911b1790565b6000611b5a6007831115610190611c38565b5060de1b600760de1b19919091161790565b6000611b7e6001841115610190611c38565b6001821b83611b905780198516611b94565b8085175b95945050505050565b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b33600090815260146020526040902054611c1d9060ff166002611c38565b565b611c3160026006541415610190611e31565b6002600655565b816108505762461bcd60e51b6000908152602060045260076024526643464923000030600a808404818106603090810160081b95839006959095019082900491820690940160101b939093010160c81b604452606490fd5b6001600655565b1c6103ff1690565b611ca7613ce1565b600083600760050154108015611cbd5750438411155b611cc75743611cc9565b835b9050611cd3612ba2565b604051631f29a8cd60e31b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063f94d466890611d42907f0000000000000000000000000000000000000000000000000000000000000000906004016145dc565b60006040518083038186803b158015611d5a57600080fd5b505afa158015611d6e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611d9691908101906140e1565b50915050611dcc81600081518110611daa57fe5b602002602001015182600181518110611dbf57fe5b6020026020010151612cb8565b925083611ddd57611ddd8383612dcb565b509250929050565b6000806000611df5600f54612cb2565b905043841415611e0e5764010000000042069150611e27565b640100000000844303600c02420381611e2357fe5b0691505b8103939092509050565b816108505761085081612e6b565b6001600160a01b038316600090815260208190526040902054611e6782821015610196611e31565b611e7e6001600160a01b0384161515610199611e31565b6001600160a01b03808516600090815260208190526040808220858503905591851681522054611eae9083612b80565b6001600160a01b038085166000818152602081815260409182902094909455805186815290519193928816927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a350505050565b611c1d7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015611f6557600080fd5b505afa158015611f79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9d9190613f4c565b6001600160a01b0316336001600160a01b0316146001611c38565b6001600160701b03607082901c811692911690565b1c60011690565b33600090815260156020526040902054611c1d906001600160a01b031615156003611c38565b60007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612067612ebe565b3060405160200180868152602001858152602001848152602001838152602001826001600160a01b031681526020019550505050505060405160208183030381529060405280519060200120905090565b6001600160601b03606082901c811692911690565b60006120dd838311156001611e31565b50900390565b6121227f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b0316146004611c38565b6108507f0000000000000000000000000000000000000000000000000000000000000000821460ce611c38565b6016546000908190612170906001600160a01b038581169116146007611c38565b60115461218b60006121838360fc611fcd565b1460e0611c38565b612196601054612ec2565b601092909255935091506121ad81600160fc611b6c565b6011556040516001600160a01b038516907f5c170c563ea37bd3580cc3b1c6d0d9e35c29182d2e26ce550917bdb7ef34bbcd906121ed90869086906146e6565b60405180910390a250915091565b600080826007600501541080156122125750438311155b61221a574392505b60115460006122298787612cb8565b9050612233611346565b6122ce576122418186612ef3565b6080810151156122745761225882600060fc611b6c565b91506122706010548260e00151836101000151612fee565b6010555b612287828260a001518360c00151612fee565b6011819055506122a4600e5482610120015183610140015161309c565b600e819055506122c1600f54826101600151836101800151613102565b600f556122ce818661317b565b80516020909101519097909650945050505050565b60008060006122f0610854565b90506122fc87876131d2565b6123108161230a878961326d565b90613291565b92506123208161230a868961326d565b9150866001600160a01b03167f998379a72dc64811ce41782d18dd7759dd22de4dd43049986376fada953be82987858560405161235f939291906146f4565b60405180910390a25094509492505050565b6000848152600b6020819052604082206001810154839261239d916001600160a01b0316151590611c38565b60018101546001600160a01b038781169116146123d25760018101546123d2906001600160a01b03878116911614600a611c38565b600181015461241e906001600160a01b0388811691161480612403575060028201546001600160a01b038881169116145b801561241757506001600160a01b03871615155b6008611c38565b600080600061242d84886132b1565b9250925092508261245e577f0000000000000000000000000000000000000000000000000000000000000000612480565b7f00000000000000000000000000000000000000000000000000000000000000005b6001600160a01b0316876124955760006124e2565b836124c0577f00000000000000000000000000000000000000000000000000000000000000006124e2565b7f00000000000000000000000000000000000000000000000000000000000000005b6001600160a01b03168560010160009054906101000a90046001600160a01b03166001600160a01b03167f4ad2915cd8cef57cf8d82983f8bf82308eb03cc31daaa0945f04baf7f2b2611585858f8f604051612541949392919061470a565b60405180910390a4826125545780612556565b815b9550826125635781612565565b805b94505050505094509492505050565b601154606090670de0b6b3a764000083116125b057600061259482612a06565b90508381146125aa576125a782856134c5565b91505b506125e8565b7fc527124565765c9f70b694614157f3cbebdb44c48483db5817d4cc5fa55e6926836040516125df91906145dc565b60405180910390a15b604080516002808252606082018352909160208301908036833701905050915061261181612ec2565b601160008660008151811061262257fe5b602002602001018760018151811061263657fe5b6020908102919091010193909352509190525550919050565b611c1d61265a611346565b156064611c38565b60006126977f000000000000000000000000000000000000000000000000000000000000000062ffffff1683111560df611c38565b60007f000000000000000000000000000000000000000000000000000000000000000061ffff1643816126c657fe5b0643908103915061ffff7f00000000000000000000000000000000000000000000000000000000000000001660018501028201908103600081878161270757fe5b0490506127186000821160d0611c38565b60078054612741908a61272c57600061272e565b835b8b612739578461273c565b60005b613102565b81556000848152600282016020526040902054612764908a61272c57600061272e565b81600201600086815260200190815260200160002081905550600760060160008154809291906001019190505595506040518060c001604052808a15158152602001836001600160701b031681526020016127c383600101548c61350b565b6001600160801b031681526020018c6001600160a01b031681526020018b6001600160a01b03168d6001600160a01b03161415612801576000612803565b8b5b6001600160a01b03908116825260209182018790526000898152600b835260408082208551815487870151888501516001600160801b0316600160781b027fff00000000000000000000000000000000ffffffffffffffffffffffffffffff6001600160701b03909216610100026effffffffffffffffffffffffffff001994151560ff19909416939093179390931691909117161781556060860151600180830180549287166001600160a01b03199384161790556080880151600284018054918816919093161790915560a090960151600390910155918f168152601783529081208054938401815581522001869055600e5482840290612919908b61290c57600061290e565b825b8c612739578361273c565b600e5550949a9950505050505050505050565b600080612949600e54876129415760ea612944565b60f45b611c97565b9050600061295c868302620186a0613291565b601154909150600061296f8260fd611fcd565b905080156129c757600061298283612a06565b9050600061299a858302670de0b6b3a7640000613291565b90506129c1848d6129ac5760006129ae565b825b8e6129b957836129bc565b60005b612fee565b60115550505b8288036000818c6129d857886129da565b895b0190506129f681838e6129ed578b6129ef565b8a5b0290613291565b9c9b505050505050505050505050565b60c01c670fffffffffffffff1690565b6000612a20611ffa565b82604051602001808061190160f01b81525060020183815260200182815260200192505050604051602081830303815290604052805190602001209050919050565b600082118015612a725750600081115b15612ab45760008183870281612a8457fe5b04905060008383890281612a9457fe5b049050612ab1868310158015612aaa5750858210155b60e4611c38565b50505b505050505050565b6000612ad46001600160a01b038716151560e2611c38565b6000612ade610854565b905080612b3757612b016103e887118015612afa57506103e886115b60cc611c38565b6103e8612b0f868802613529565b039150612b1e600f544261357a565b600f55612b2e60006103e86135b8565b43600c55612b5e565b612b5b612b488561230a898561326d565b612b568561230a898661326d565b613649565b91505b612b6c6000831160cc611c38565b612b7687836135b8565b5095945050505050565b6000828201612b928482101583611e31565b9392505050565b60de1c60071690565b604080516001808252818301909252600091816020015b612bc1613d8c565b815260200190600190039081612bb9579050509050600181600081518110612be557fe5b6020026020010151600001906003811115612bfc57fe5b90816003811115612c0957fe5b815250503081600081518110612c1b57fe5b60209081029190910101516001600160a01b039182166060909101526040516303a38fa160e21b81527f000000000000000000000000000000000000000000000000000000000000000090911690630e8e3e8490612c7d908490600401614501565b600060405180830381600087803b158015612c9757600080fd5b505af1158015612cab573d6000803e3d6000fd5b5050505050565b60e01c90565b612cc0613ce1565b600080612ccd858561365f565b60115491935091506000612ce28260fe611fcd565b90506000612cf18360fd611fcd565b90506000612cfe84612a06565b9050600082612d1557670de0b6b3a7640000612d21565b81670de0b6b3a7640000035b90506000808515612d4c57612d37601054612b99565b91508160020a6001018381612d4857fe5b0490505b604051806101e001604052808a81526020018981526020018481526020018281526020018381526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815250995050505050505050505092915050565b6000612dda600e5460e0611c97565b9050600080612de76136f7565b815191935091505b84821015612e4957612e0386838587613772565b612e0e86828461386c565b508151612e1b82846138d8565b7f000000000000000000000000000000000000000000000000000000000000000061ffff1682019150612def565b82518514612ab457849150612e6086838587613772565b612ab486828761386c565b62461bcd60e51b6000908152602060045260076024526642414c23000030600a808404818106603090810160081b95839006959095019082900491820690940160101b939093010160c81b604452606490fd5b4690565b77ffffffffffffffffffffffffffffffffffffffffffffffff198116916001600160601b03606083901c8116921690565b6000612f02600e5460e0611c97565b9050600080612f0f6136f7565b815191935091505b84821015612f7b57612f2b86838587613772565b612f3686828461386c565b508151612f4382846138d8565b612f4d8383613946565b7f000000000000000000000000000000000000000000000000000000000000000061ffff1682019150612f17565b82518514612fb157849150612f9286838587613772565b612f9d86828761386c565b612fa782846138d8565b612fb18383613946565b8251600c5560608301516080840151612fcc9160009161396b565b60075560208301516040840151612fe391906139b2565b600855505050505050565b60006001600160601b03606085901c811690851684156130395761301b8583600019031015610191611c38565b908401906001600160601b03821115613039576001600160601b0391505b831561306d576130528482600019031015610191611c38565b83016001600160601b0381111561306d57506001600160601b035b77ffffffffffffffffffffffffffffffffffffffffffffffff199590951660609190911b179093179392505050565b60006001600160701b03607085901c811690851684156130cb576130c585831015610192611c38565b84820391505b83156130e4576130e084821015610192611c38565b8390035b6001600160e01b03199590951660709190911b179093179392505050565b60006001600160701b03607085901c8116908516841561313b5761313685836001600160701b038016031015610191611c38565b908401905b83156130e45761315b84826001600160701b038016031015610191611c38565b83016001600160e01b03199590951660709190911b179093179392505050565b60008061318783611de5565b909250905063ffffffff8216156131b7576101a08401516012805490910190556101c08401516013805490910190555b6131c9600f548263ffffffff1661357a565b600f5550505050565b6001600160a01b0382166000908152602081905260409020546131fa82821015610196611e31565b6001600160a01b0383166000908152602081905260409020828203905560025461322490836120cd565b6002556040805183815290516000916001600160a01b038616917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a3505050565b6000828202612b9284158061328a57508385838161328757fe5b04145b6003611e31565b60006132a08215156004611e31565b8183816132a957fe5b049392505050565b8154600383015460ff821691600091829161010090046001600160701b031685156132f057600c546132e690831160e3611c38565b600c548203810293505b60006133248715801561330257508343115b61330e5760085461331e565b6000848152600a60205260409020545b8761350b565b8854909150613346908290600160781b90046001600160801b031684896139eb565b9350861580156133565750824311155b156133935787547fff00000000000000000000000000000000ffffffffffffffffffffffffffffff16600160781b6001600160801b038316021788555b6133ac60008611806133a55750600085115b60d4611c38565b861561343457600780546133db90886133c65760006133c8565b845b896133d357856133d6565b60005b61309c565b815560008481526002820160205260409020546133fe90886133c65760006133c8565b6000858152600283016020526040902055600e5461342f9088613422576000613424565b875b896133d357886133d6565b600e55505b61345a600f54876134455785613448565b60005b886134545760006133d6565b8661309c565b600f55868061346857508243115b156134bb5787547fff000000000000000000000000000000000000000000000000000000000000001688556001880180546001600160a01b03199081169091556002890180549091169055600060038901555b5050509250925092565b60006134de670fffffffffffffff831115610190611c38565b5060c01b7ff000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff919091161790565b600081613521576001600160801b038316612b92565b505060801c90565b6000600382111561356c575080600160028204015b818110156135665780915060028182858161355557fe5b04018161355e57fe5b04905061353e565b50610d26565b8115610d2657506001919050565b600061358f63ffffffff831115610190611c38565b5060e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff919091161790565b6001600160a01b0382166000908152602081905260409020546135db9082612b80565b6001600160a01b0383166000908152602081905260409020556002546136019082612b80565b6002556040805182815290516001600160a01b038416916000917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a35050565b60008183106136585781612b92565b5090919050565b600080600080613670600e54611fb8565b91509150600080613682600f54611fb8565b6011549193509150600080613696836120b8565b9150915060008060006136aa8660fc611fcd565b9050806136c2576136bc6010546120b8565b90935091505b6136d28e8b8a01870185016120cd565b9b506136e48d8a8901860184016120cd565b9a50505050505050505050509250929050565b6136ff613dbc565b600c5460009061ffff7f00000000000000000000000000000000000000000000000000000000000000001680828161373357fe5b838652600754919006830391909101925061374d90611fb8565b6080850152606084015260085461376390613a5b565b60408501526020840152509091565b815160608301516080840151918503918183026000613796868302620186a0613a6f565b905082850260006137ac888302620186a0613a6f565b90506000806137bc8d8685613aa2565b915091506000806137d28f888a03878903613ba2565b91509150878f610120018181510191508181525050858f6101400181815101915081815250506138106001600160701b038016831115610191611c38565b6138266001600160701b03821115610191611c38565b6101608f018051830190526101808f018051820190528e5184018f5260208f018051840190526138598d83838d8d613c45565b5050509990985250505050505050505050565b8251600c8383030290156138a1578351602085015182919060701b8161388e57fe5b0402846101a00181815101915081815250505b6020840151156138d2576020840151845182919060701b816138bf57fe5b0402846101c00181815101915081815250505b50505050565b60008281526009602052604081205481906138f290611fb8565b9092509050811561391c576139108284606001511015610192611c38565b60608301805183900390525b80156138d2576139358184608001511015610192611c38565b608083018051829003905250505050565b613958826020015183604001516139b2565b6000918252600a60205260409091205550565b60006139836001600160701b03841115610190611c38565b6139996001600160701b03831115610190611c38565b506001600160e01b03199290921660709190911b171790565b60006139ca6001600160801b03841115610190611c38565b6139e06001600160801b03831115610190611c38565b5060809190911b1790565b60006001600160801b038486031682613a24577f0000000000000000000000000000000000000000000000000000000000000000613a46565b7f00000000000000000000000000000000000000000000000000000000000000005b84820281613a5057fe5b049695505050505050565b608081901c916001600160801b0390911690565b6000613a7e8215156004611e31565b82613a8b5750600061073a565b816001840381613a9757fe5b04600101905061073a565b60808301516040840151600091829181613b1057613aca868202670de0b6b3a7640000613291565b9350613ae0858202670de0b6b3a7640000613291565b9250670de0b6b3a7640000811015613b0b5760a08701805185880301905260c0870180518487030190525b613b98565b60608701516000613b2b888302670de0b6b3a7640000613291565b90506000613b43888402670de0b6b3a7640000613291565b82861b975080861b96509050670de0b6b3a7640000841015613b7e5760a08a018051888b0384900301905260c08a018051878a038390030190525b60e08a018051909201909152610100890180519091019052505b5050935093915050565b60008083151580613bb257508215155b15613c3d578451602086015185613bdd5784018082860281613bd057fe5b0493508382039150613c34565b84613bfc57908501908181870281613bf157fe5b049250829003613c34565b81860181860160008184840281613c0f57fe5b04905060008383870281613c1f57fe5b94839003985090930491829003955093509150505b90865260208601525b935093915050565b8115613c9057817f0000000000000000000000000000000000000000000000000000000000000000840281613c7657fe5b048560200151016001600160801b03168560200181815250505b8015612cab57807f0000000000000000000000000000000000000000000000000000000000000000850281613cc157fe5b048560400151016001600160801b03168560400181815250505050505050565b604051806101e001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b6040805160a081019091528060008152600060208201819052604082018190526060820181905260809091015290565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b8035610d26816147f1565b600082601f830112613e06578081fd5b81356020613e1b613e16836147c9565b6147a5565b8281528181019085830183850287018401881015613e37578586fd5b855b85811015613e5557813584529284019290840190600101613e39565b5090979650505050505050565b600082601f830112613e72578081fd5b81516020613e82613e16836147c9565b8281528181019085830183850287018401881015613e9e578586fd5b855b85811015613e5557815184529284019290840190600101613ea0565b600082601f830112613ecc578081fd5b813567ffffffffffffffff811115613ee057fe5b613ef3601f8201601f19166020016147a5565b818152846020838601011115613f07578283fd5b816020850160208301379081016020019190915292915050565b803560028110610d2657600080fd5b600060208284031215613f41578081fd5b8135612b92816147f1565b600060208284031215613f5d578081fd5b8151612b92816147f1565b60008060408385031215613f7a578081fd5b8235613f85816147f1565b91506020830135613f95816147f1565b809150509250929050565b600080600060608486031215613fb4578081fd5b8335613fbf816147f1565b92506020840135613fcf816147f1565b929592945050506040919091013590565b600080600080600080600060e0888a031215613ffa578485fd5b8735614005816147f1565b96506020880135614015816147f1565b95506040880135945060608801359350608088013560ff81168114614038578384fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215614067578182fd5b8235614072816147f1565b91506020830135613f9581614806565b60008060408385031215614094578182fd5b823561409f816147f1565b946020939093013593505050565b6000806000606084860312156140c1578081fd5b83356140cc816147f1565b95602085013595506040909401359392505050565b6000806000606084860312156140f5578081fd5b835167ffffffffffffffff8082111561410c578283fd5b818601915086601f83011261411f578283fd5b8151602061412f613e16836147c9565b82815281810190858301838502870184018c101561414b578788fd5b8796505b84871015614176578051614162816147f1565b83526001969096019591830191830161414f565b509189015191975090935050508082111561418f578283fd5b5061419c86828701613e62565b925050604084015190509250925092565b6000602082840312156141be578081fd5b8135612b9281614806565b6000602082840312156141da578081fd5b8151612b9281614806565b60008060008060008060008060e0898b031215614200578182fd5b883597506020890135614212816147f1565b96506040890135614222816147f1565b9550606089013567ffffffffffffffff8082111561423e578384fd5b61424a8c838d01613df6565b965060808b0135955060a08b0135945060c08b013591508082111561426d578384fd5b818b0191508b601f830112614280578384fd5b81358181111561428e578485fd5b8c602082850101111561429f578485fd5b6020830194508093505050509295985092959890939650565b6000806000606084860312156142cc578081fd5b833567ffffffffffffffff808211156142e3578283fd5b81860191506101208083890312156142f9578384fd5b614302816147a5565b905061430d83613f21565b815261431b60208401613deb565b602082015261432c60408401613deb565b6040820152606083013560608201526080830135608082015260a083013560a082015261435b60c08401613deb565b60c082015261436c60e08401613deb565b60e08201526101008084013583811115614384578586fd5b6143908a828701613ebc565b9183019190915250976020870135975060409096013595945050505050565b6000602082840312156143c0578081fd5b5035919050565b6000806000606084860312156143db578081fd5b83359250602084013567ffffffffffffffff808211156143f9578283fd5b61440587838801613df6565b9350604086013591508082111561441a578283fd5b5061442786828701613df6565b9150509250925092565b60008060408385031215614443578182fd5b823591506020830135613f9581614806565b60008060408385031215614467578182fd5b50508035926020909101359150565b60008060408385031215614488578182fd5b505080516020909101519092909150565b6000815180845260208085019450808401835b838110156144c8578151875295820195908201906001016144ac565b509495945050505050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b602080825282518282018190526000919060409081850190868401855b8281101561457a57815180516004811061453457fe5b8552808701516001600160a01b03908116888701528682015187870152606080830151821690870152608091820151169085015260a0909301929085019060010161451e565b5091979650505050505050565b60006040825261459a6040830185614499565b8281036020840152611b948185614499565b6000606082526145bf6060830186614499565b60208301949094525060400152919050565b901515815260200190565b90815260200190565b60408101600284106145f357fe5b92815290151560209091015290565b6040810161460f846147e7565b9281526020015290565b60208101614626836147e7565b91905290565b6000602080835283518082850152825b818110156146585785810183015185820160400152820161463c565b818111156146695783604083870101525b50601f01601f1916929092016040019392505050565b600060c0820190508251151582526001600160701b0360208401511660208301526001600160801b03604084015116604083015260608301516001600160a01b038082166060850152806080860151166080850152505060a083015160a083015292915050565b918252602082015260400190565b9283526020830191909152604082015260600190565b938452602084019290925260408301526001600160a01b0316606082015260800190565b93845260208401929092526040830152606082015260800190565b9a8b5260208b019990995260408a01979097526060890195909552608088019390935260a087019190915260c086015260e08501526101008401526101208301526101408201526101600190565b60ff91909116815260200190565b60405181810167ffffffffffffffff811182821017156147c157fe5b604052919050565b600067ffffffffffffffff8211156147dd57fe5b5060209081020190565b600381106109d657fe5b6001600160a01b03811681146109d657600080fd5b80151581146109d657600080fdfea2646970667358221220e5d32adddd65b1ba95d5b33b5251a34884a7f14f2bef5dd0fca9d439a916328864736f6c63430007060033a26469706673582212200199b0c67f83d69c78a57b9bde89d3296c0157a2da8e8cf19ca932350f91512264736f6c63430007060033000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8
Deployed Bytecode
0x60806040523480156200001157600080fd5b5060043610620000d05760003560e01c80638da5cb5b1162000081578063a53253431162000063578063a532534314620001ff578063acf6cc1a1462000350578063e30c3978146200038957620000d0565b80638da5cb5b14620001b65780638f62b25c14620001c057620000d0565b806359e5fd0411620000b757806359e5fd04146200011a5780636634b75314620001535780638d928af8146200019057620000d0565b8063078dfbe714620000d55780634e71e0c81462000110575b600080fd5b6200010e60048036036060811015620000ed57600080fd5b506001600160a01b0381351690602081013515159060400135151562000393565b005b6200010e62000460565b6200010e600480360360608110156200013257600080fd5b506001600160a01b03813581169160208101359091169060400135620004df565b6200017c600480360360208110156200016b57600080fd5b50356001600160a01b0316620005ef565b604080519115158252519081900360200190f35b6200019a6200060d565b604080516001600160a01b039092168252519081900360200190f35b6200019a62000631565b6200010e60048036036080811015620001d857600080fd5b506001600160a01b0381358116916020810135821691604082013591606001351662000640565b6200019a600480360360a08110156200021757600080fd5b6001600160a01b0382358116926020810135909116918101906060810160408201356401000000008111156200024c57600080fd5b8201836020820111156200025f57600080fd5b803590602001918460018302840111640100000000831117156200028257600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050640100000000811115620002d657600080fd5b820183602082011115620002e957600080fd5b803590602001918460018302840111640100000000831117156200030c57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550509135925062000700915050565b6200019a600480360360608110156200036857600080fd5b506001600160a01b03813581169160208101359091169060400135620009bd565b6200019a62000a21565b600154620003af906001600160a01b031633146101f762000a30565b81156200043f57620003d86001600160a01b038416151580620003cf5750815b6101f462000a30565b6001546040516001600160a01b038086169216907fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c90600090a3600180546001600160a01b0385166001600160a01b0319918216179091556002805490911690556200045b565b600280546001600160a01b0319166001600160a01b0385161790555b505050565b6002546001600160a01b03166200047c3382146101f862000a30565b6001546040516001600160a01b038084169216907fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c90600090a3600180546001600160a01b039092166001600160a01b0319928316179055600280549091169055565b600154620004fb906001600160a01b031633146101f762000a30565b6001600160a01b03808416600090815260036020908152604080832086851684528252808320858452909152902054166200053b8115156101f962000a30565b6001600160a01b0380851660009081526003602090815260408083209387168352928152828220858352905290812080546001600160a01b03191690558260028111156200058557fe5b9050836001600160a01b0316856001600160a01b0316836001600160a01b03167fec6e3eb5f34b5df1c942ddf42cdd59bd65528ffad1dc521e7b51b0f96eb0e4338460405180826002811115620005d857fe5b815260200191505060405180910390a45050505050565b6001600160a01b031660009081526020819052604090205460ff1690565b7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c890565b6001546001600160a01b031681565b6001546200065c906001600160a01b031633146101f762000a30565b6001600160a01b03848116600090815260036020908152604080832087851684528252808320868452909152812080546001600160a01b03191692841692909217909155826002811115620006ad57fe5b9050836001600160a01b0316856001600160a01b0316836001600160a01b03167fd9bdd19c2f64837c4d11b9979d041e4f60cf82f7e3b68a9f80f919ebfe8986c68460405180826002811115620005d857fe5b6000808260028111156200071057fe5b905062000735866001600160a01b0316886001600160a01b031614156101f562000a30565b600080876001600160a01b0316896001600160a01b0316106200075a5787896200075d565b88885b90925090506200077b6001600160a01b03831615156101f462000a30565b6001600160a01b03828116600090815260036020908152604080832085851684528252808320898452909152902054620007ba9116156101f662000a30565b60008282620007c86200060d565b8a8a88604051620007d99062000ad9565b80876001600160a01b03168152602001866001600160a01b03168152602001856001600160a01b0316815260200180602001806020018460028111156200081c57fe5b8152602001838103835286818151815260200191508051906020019080838360005b83811015620008585781810151838201526020016200083e565b50505050905090810190601f168015620008865780820380516001836020036101000a031916815260200191505b50838103825285518152855160209182019187019080838360005b83811015620008bb578181015183820152602001620008a1565b50505050905090810190601f168015620008e95780820380516001836020036101000a031916815260200191505b5098505050505050505050604051809103906000f08015801562000911573d6000803e3d6000fd5b5090506200091f8162000a8d565b6001600160a01b038381166000908152600360209081526040808320868516845282528083208a84529091529081902080546001600160a01b03191684841690811790915590518b8316928d1691907faeff84bd0403e2418c457955d4258f28133c5b304b671114fc854725bb098bee90889080826002811115620009a057fe5b815260200191505060405180910390a49998505050505050505050565b6000806000846001600160a01b0316866001600160a01b031610620009e4578486620009e7565b85855b6001600160a01b03918216600090815260036020908152604080832093851683529281528282208883529052205416925050509392505050565b6002546001600160a01b031681565b8162000a895762461bcd60e51b6000908152602060045260076024526643464923000030600a808404818106603090810160081b95839006959095019082900491820690940160101b939093010160c81b604452606490fd5b5050565b6001600160a01b038116600081815260208190526040808220805460ff19166001179055517f83a48fbcfc991335314e74d0496aab6a1987e992ddc85dddbcc4d6dd6ef2e9fc9190a250565b6154a98062000ae88339019056fe6102406040527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c960e0523480156200003657600080fd5b50604051620054a9380380620054a98339810160408190526200005991620009a4565b6040805180820190915260018152603160f81b602080830191825285519086019081206080529151902060a0527f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60c052835184918491620000be9160039162000844565b508051620000d490600490602084019062000844565b50506001600655503360601b610220526040516309b2760f60e01b81526000906001600160a01b038616906309b2760f90620001169060029060040162000b32565b602060405180830381600087803b1580156200013157600080fd5b505af115801562000146573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200016c91906200098b565b60408051600280825260608201835292935060009290916020830190803683370190505090508781600081518110620001a157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508681600181518110620001d057fe5b6001600160a01b03928316602091820292909201015260408051600280825260608201909252918816916366a9c7d29185918591816020016020820280368337019050506040518463ffffffff1660e01b8152600401620002349392919062000a98565b600060405180830381600087803b1580156200024f57600080fd5b505af115801562000264573d6000803e3d6000fd5b5050506001600160601b0319606088811b8216610100526101208590528a811b82166101405289901b166101605250826002811115620002a057fe5b610180816002811115620002b057fe5b60f81b815250506000886001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015620002f357600080fd5b505afa15801562000308573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200032e919062000a63565b60ff16905062000352816002111580156200034a575060168211155b60e162000766565b80600101600a0a6101e081815250506000886001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156200039d57600080fd5b505afa158015620003b2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003d8919062000a63565b60ff169050620003fb816002111580156200034a5750601682111560e162000766565b60018101600a0a6102005260008560028111156200041557fe5b14620004435760018560028111156200042a57fe5b1462000439576104b06200043d565b61012c5b62000446565b604b5b60f01b6001600160f01b0319166101a05260008560028111156200046657fe5b14620004945760018560028111156200047b57fe5b146200048a57612acd6200048e565b61ab365b62000499565b6202acd85b60e81b6001600160e81b0319166101c0526000856002811115620004b957fe5b14156200052b576000620004e16000600a61ffff1660f4620007c360201b62001b241760201c565b90506200050181600561ffff1660ea620007c360201b62001b241760201c565b90506200052181601e61ffff1660e0620007c360201b62001b241760201c565b600e555062000609565b60018560028111156200053a57fe5b1415620005a2576000620005626000603261ffff1660f4620007c360201b62001b241760201c565b90506200058281601961ffff1660ea620007c360201b62001b241760201c565b90506200052181609661ffff1660e0620007c360201b62001b241760201c565b6000620005c36000606461ffff1660f4620007c360201b62001b241760201c565b9050620005e381603261ffff1660ea620007c360201b62001b241760201c565b9050620006048161012c61ffff1660e0620007c360201b62001b241760201c565b600e55505b62000626601054600160ff16620007e960201b62001b481760201c565b60108190555060006200064860008060ff6200080f60201b62001b6c1760201c565b90506200066481600060fe6200080f60201b62001b6c1760201c565b90506200068081600160fd6200080f60201b62001b6c1760201c565b90506200069c81600160fc6200080f60201b62001b6c1760201c565b60115573e122eff60083bc550acbf31e7d8197a58d436b39600081905260146020527f65af30822488a24bf4917ce195d1b124a7dbfde2fa518b38c26d567c783e216e805460ff1916600190811790915560405133917fe8b9bbf829bebf34c09fc3a715a8e78509552d2d567e7682e24229f1c16693059162000720919062000a8d565b60405180910390a360405160009033907fb5c9ea0e9f459f0f9f5e75158bb0d1e656d758ede76198f5843b070d1676e517908390a3505050505050505050505062000b60565b81620007bf5762461bcd60e51b6000908152602060045260076024526643464923000030600a808404818106603090810160081b95839006959095019082900491820690940160101b939093010160c81b604452606490fd5b5050565b6000620007d86103ff84111561019062000766565b506103ff811b1992909216911b1790565b6000620007fd600783111561019062000766565b5060de1b600760de1b19919091161790565b600062000823600184111561019062000766565b6001821b836200083757801985166200083b565b8085175b95945050505050565b828054600181600116156101000203166002900490600052602060002090601f0160209004810192826200087c5760008555620008c7565b82601f106200089757805160ff1916838001178555620008c7565b82800160010185558215620008c7579182015b82811115620008c7578251825591602001919060010190620008aa565b50620008d5929150620008d9565b5090565b5b80821115620008d55760008155600101620008da565b600082601f83011262000901578081fd5b81516001600160401b03808211156200091657fe5b6040516020601f8401601f19168201810183811183821017156200093657fe5b60405283825285840181018710156200094d578485fd5b8492505b8383101562000970578583018101518284018201529182019162000951565b838311156200098157848185840101525b5095945050505050565b6000602082840312156200099d578081fd5b5051919050565b60008060008060008060c08789031215620009bd578182fd5b8651620009ca8162000b47565b6020880151909650620009dd8162000b47565b6040880151909550620009f08162000b47565b60608801519094506001600160401b038082111562000a0d578384fd5b62000a1b8a838b01620008f0565b9450608089015191508082111562000a31578384fd5b5062000a4089828a01620008f0565b92505060a08701516003811062000a55578182fd5b809150509295509295509295565b60006020828403121562000a75578081fd5b815160ff8116811462000a86578182fd5b9392505050565b901515815260200190565b60006060820185835260206060818501528186518084526080860191508288019350845b8181101562000ae35784516001600160a01b03168352938301939183019160010162000abc565b505084810360408601528551808252908201925081860190845b8181101562000b245782516001600160a01b03168552938301939183019160010162000afd565b509298975050505050505050565b602081016003831062000b4157fe5b91905290565b6001600160a01b038116811462000b5d57600080fd5b50565b60805160a05160c05160e0516101005160601c610120516101405160601c6101605160601c6101805160f81c6101a05160f01c6101c05160e81c6101e051610200516102205160601c61484a62000c5f60003980611f0e525080613a005280613c99525080613a265280613c4e52508061266952508061269b52806126d25280612e1d5280612f4f528061370a525080610c90525080612460528061249c5250806110e2528061243a52806124c25250806119675280611a035280611d1a528061212752508061105d52806119d65280611ced52806120e85280612c4652508061155f525080611ffe52508061204052508061201f525061484a6000f3fe608060405234801561001057600080fd5b506004361061032b5760003560e01c80638705fcd4116101b2578063d505accf116100f9578063e0b20d6c116100a2578063ee7293561161007c578063ee72935614610658578063f449dbc814610660578063fa6c4b4f14610668578063fca513a81461067b5761032b565b8063e0b20d6c14610640578063e0d7d0e914610648578063e84b1f3f146106505761032b565b8063dd62ed3e116100d3578063dd62ed3e14610612578063ddba751414610625578063df0f11af1461062d5761032b565b8063d505accf146105d9578063d5c096c4146105ec578063d73dd623146105ff5761032b565b8063b187bd261161015b578063bedb86fb11610135578063bedb86fb14610584578063bf37843c14610597578063d09ef241146105b95761032b565b8063b187bd261461056c578063b4456b3614610574578063bc36419f1461057c5761032b565b8063a511eb7c1161018c578063a511eb7c1461053e578063a6c449c114610546578063a9059cbb146105595761032b565b80638705fcd41461051057806395d89b41146105235780639d2c110c1461052b5761032b565b80633644e5151161027657806374f3b0091161021f5780637ca2c357116101f95780637ca2c357146104ed5780637ecebe00146104f55780637f6de94b146105085761032b565b806374f3b009146104b157806377f53c2f146104d257806378b45070146104da5761032b565b8063648da85911610250578063648da85914610476578063661884631461048b57806370a082311461049e5761032b565b80633644e515146104315780634f5bee44146104395780634ff643211461044c5761032b565b806323b872dd116102d857806330c55ddb116102b257806330c55ddb146103ff57806330c5ee4614610407578063313ce5671461041c5761032b565b806323b872dd146103c357806327ca08a2146103d65780633023f7c8146103e95761032b565b806318160ddd1161030957806318160ddd146103835780631822a4211461039857806319ebefa7146103a05761032b565b806306fdde0314610330578063095ea7b31461034e57806312ee63f31461036e575b600080fd5b610338610692565b604051610345919061462c565b60405180910390f35b61036161035c366004614082565b610729565b60405161034591906145d1565b61038161037c366004614455565b610740565b005b61038b610854565b60405161034591906145dc565b61038b61085a565b6103b36103ae3660046143af565b61086e565b604051610345949392919061472e565b6103616103d1366004613fa0565b6108de565b6103816103e43660046141ad565b61095f565b6103f16109d9565b6040516103459291906146e6565b6103616109f0565b61040f610a07565b60405161034591906144d3565b610424610b22565b6040516103459190614797565b61038b610b27565b610381610447366004614055565b610b31565b61045f61045a366004614431565b610ba7565b6040516103459b9a99989796959493929190614749565b61047e610c8e565b6040516103459190614619565b610361610499366004614082565b610cb2565b61038b6104ac366004613f30565b610d0c565b6104c46104bf3660046141e5565b610d2b565b604051610345929190614587565b61038b610ea4565b6103816104e83660046143af565b610eaa565b61038b610f18565b61038b610503366004613f30565b610f27565b6103f1610f42565b61038161051e366004613f30565b610f50565b610338610fe4565b61038b6105393660046142b8565b611045565b6103f16112fd565b6103f16105543660046143af565b611315565b610361610567366004614082565b611339565b610361611346565b61038b611356565b6103f1611363565b6103816105923660046141ad565b611371565b6105aa6105a53660046140ad565b6113d8565b604051610345939291906145ac565b6105cc6105c73660046143af565b6114a8565b604051610345919061467f565b6103816105e7366004613fe0565b611530565b6104c46105fa3660046141e5565b6116a0565b61036161060d366004614082565b611864565b61038b610620366004613f68565b61189a565b6103616118c5565b61038161063b366004613f68565b6118d5565b61038b611958565b61038b611965565b61038b611989565b6103f161198f565b61038b61199d565b6103816106763660046143af565b6119ac565b610683611b02565b604051610345939291906146f4565b60038054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561071e5780601f106106f35761010080835404028352916020019161071e565b820191906000526020600020905b81548152906001019060200180831161070157829003601f168201915b505050505090505b90565b6000610736338484611b9d565b5060015b92915050565b610748611bff565b610750611c1f565b600082600281111561075e57fe5b905060008082600281111561076f57fe5b141561077d575060f46107b1565b600182600281111561078b57fe5b1415610799575060ea6107b1565b60028260028111156107a757fe5b14156107b1575060e05b8260028360028111156107c057fe5b111580156107cd57508115155b80156107db57506103e88411155b156107f5576107ed600e548284611b24565b600e55610802565b6108026000610193611c38565b336001600160a01b03167f225ac322492a1d8feb500fcd29c839e1e846186ea09a2f37d3bb8974f4e85f45848360405161083d929190614602565b60405180910390a2505050610850611c90565b5050565b60025490565b6000610869600e5460e0611c97565b905090565b60008060008061087c613ce1565b610887866000611c9f565b600c54909350909150156108d65760006108a083611de5565b60125460135463ffffffff928316995090975095509091508116156108d457816101a0015185019450816101c00151840193505b505b509193509193565b6001600160a01b0383166000818152600160209081526040808320338085529252822054919261091c9114806109145750838210155b610197611e31565b610927858585611e3f565b336001600160a01b0386161480159061094257506000198114155b15610954576109548533858403611b9d565b506001949350505050565b610967611f09565b61096f611c1f565b61098e60115482610981576000610984565b60015b60ff1660fd611b6c565b60115560405133907f9d7e9829e0e608d50be70896ea553fe5ba9db33c11a836495412b8b8481aa4dd906109c69060019085906145e5565b60405180910390a26109d6611c90565b50565b6000806109e7600f54611fb8565b90939092509050565b600080610a0060115460fd611fcd565b1415905090565b6000610a11611fd4565b610a19611c1f565b336000908152601560205260408120546001600160a01b0316805b809250826001600160a01b03166355fcff3c6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015610a7257600080fd5b505af1158015610a86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aaa9190613f4c565b90506001600160a01b038116610a34573360008181526015602052604080822080546001600160a01b0319166001600160a01b03888116918217909255915191939086169290917fe0e07986073e388ace1090c16866621627f1f1299f7aa008589d4d45b61034ab9190a45090915050610726611c90565b601290565b6000610869611ffa565b610b39611f09565b610b41611c1f565b6001600160a01b03821660008181526014602052604090819020805460ff19168415151790555133907fe8b9bbf829bebf34c09fc3a715a8e78509552d2d567e7682e24229f1c166930590610b979085906145d1565b60405180910390a3610850611c90565b6000806000806000806000806000806000610bc0613ce1565b610bca8e8e611c9f565b809d50819250505080600001519a5080602001519950600080610bee600e54611fb8565b9150915082610120015182039a5082610140015181039950600080610c14600f54611fb8565b9150915084610160015182019a5084610180015181019950600080610c3a6011546120b8565b915091508660a0015182019a508660c0015181019950600080610c5e6010546120b8565b915091508860e0015182019a50886101000151810199505050505050505050509295989b509295989b9093969950565b7f000000000000000000000000000000000000000000000000000000000000000081565b3360009081526001602090815260408083206001600160a01b0386168452909152812054808310610cee57610ce933856000611b9d565b610d02565b610d023385610cfd84876120cd565b611b9d565b5060019392505050565b6001600160a01b0381166000908152602081905260409020545b919050565b606080610d38338b6120e3565b60008080610d4886880188614455565b92509050806003811115610d5857fe5b925060009050806003846003811115610d6d57fe5b1415610d8657610d7c8d61214f565b9092509050610e34565b600080610dbc8d600081518110610d9957fe5b60200260200101518e600181518110610dae57fe5b6020026020010151436121fb565b90925090506000866003811115610dcf57fe5b1415610de757610de18f8684846122e3565b90945092505b5060019050846003811115610df857fe5b1480610e0f57506002846003811115610e0d57fe5b145b15610e3457610e2e838e8e6002886003811115610e2857fe5b14612371565b90925090505b60408051600280825260608201835290916020830190803683370190505095508186600081518110610e6257fe5b6020026020010181815250508086600181518110610e7c57fe5b602002602001018181525050610e9189612574565b9450505050509850989650505050505050565b600c5490565b610eb2611f09565b610eba611c1f565b610ed760018210158015610ecf575060048211155b610193611c38565b610ee360105482611b48565b60105560405133907f5acdc353c39f707c917be1fcdfd9f0ef222602d718c114688a00abd2fc80e8fd906109c69084906145dc565b6000610869600e5460f4611c97565b6001600160a01b031660009081526005602052604090205490565b6000806109e76010546120b8565b610f58611f09565b610f60611c1f565b601680546001600160a01b0319166001600160a01b038316908117909155600090610f8c576000610f8f565b60015b60ff169050610fa26011548260fe611b6c565b6011556040516001600160a01b0383169033907fb5c9ea0e9f459f0f9f5e75158bb0d1e656d758ede76198f5843b070d1676e51790600090a3506109d6611c90565b60048054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561071e5780601f106106f35761010080835404028352916020019161071e565b600061104f61264f565b611085336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146004611c38565b602084015160c085015160608601516110ae6000885160018111156110a657fe5b1460c9611c38565b6000808861010001518060200190518101906110ca9190614476565b9150915060008260028111156110dc57fe5b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316876001600160a01b031614905060008061113d83611128578b61112a565b8c5b84611135578d611137565b8c5b436121fb565b9092509050600184600281111561115057fe5b14156111cc5760e08d0151600061116a8a83878c8b612662565b90508a6001600160a01b0316826001600160a01b03168b6001600160a01b03167f5aba2dae7ef407628bfbaf988767303f2c5ac508d40c737736efe9536c52fbeb8c8b866040516111bd939291906146f4565b60405180910390a450506112ed565b60028460028111156111da57fe5b1415611280576001600160a01b038086166000908152601560205260409081902054905163387f517760e01b815291169061127e90829063387f517790611225908d906004016144d3565b602060405180830381600087803b15801561123f57600080fd5b505af1158015611253573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061127791906141c9565b6005611c38565b505b61129b83600086600281111561129257fe5b1489858561292c565b9950886001600160a01b0316886001600160a01b03167f37e41badd72aca9bedfbfa5f3c99a2b3b408e7b966e604063a37af67cd081d10898d8a6040516112e4939291906146f4565b60405180910390a35b5050505050505050509392505050565b6007546001600160701b03607082901c811692911690565b600081815260096020526040812054819061132f90611fb8565b9094909350915050565b6000610736338484611e3f565b600080610a0060115460ff611fcd565b6000610869601154612a06565b6000806109e7600e54611fb8565b611379611bff565b611381611c1f565b6113a060115482611393576000611396565b60015b60ff1660ff611b6c565b60115560405133907f9d7e9829e0e608d50be70896ea553fe5ba9db33c11a836495412b8b8481aa4dd906109c69060009085906145e5565b6001600160a01b03831660009081526017602052604081208054606092918285156114035785611405565b825b90508067ffffffffffffffff8111801561141e57600080fd5b50604051908082528060200260200182016040528015611448578160200160208202803683370190505b509450865b818510801561145b57508381105b1561149c5782818154811061146c57fe5b906000526020600020015486868060010197508151811061148957fe5b602090810291909101015260010161144d565b50505093509350939050565b6114b0613d57565b506000908152600b6020908152604091829020825160c081018452815460ff81161515825261010081046001600160701b031693820193909352600160781b9092046001600160801b03169282019290925260018201546001600160a01b039081166060830152600283015416608082015260039091015460a082015290565b61153e8442111560d1611e31565b6001600160a01b0380881660008181526005602090815260408083205481517f00000000000000000000000000000000000000000000000000000000000000008185015280830195909552948b166060850152608084018a905260a0840185905260c08085018a90528151808603909101815260e090940190528251920191909120906115ca82612a16565b9050600060018288888860405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611628573d6000803e3d6000fd5b5050604051601f190151915061166a90506001600160a01b0382161580159061166257508b6001600160a01b0316826001600160a01b0316145b6101f8611e31565b6001600160a01b038b1660009081526005602052604090206001850190556116938b8b8b611b9d565b5050505050505050505050565b6060806116ad338b6120e3565b60006060816116be868801886143c7565b909650925090508060018111156116d157fe5b9250506000846000815181106116e357fe5b602002602001015190506000856001815181106116fc57fe5b602002602001015190506000611710611346565b6117de576000808087600181111561172457fe5b1480156117375750611734610854565b15155b8061174d5750600187600181111561174b57fe5b145b1561177d576117778e60008151811061176257fe5b60200260200101518f600181518110610dae57fe5b90925090505b600087600181111561178b57fe5b14156117d7576117c78585886000815181106117a357fe5b6020026020010151896001815181106117b857fe5b60200260200101518686612a62565b6117d48f86868585612abc565b92505b50506117f8565b6117f860008660018111156117ef57fe5b14156064611c38565b8c6001600160a01b03168e6001600160a01b03167f54f5163abddef0c7b641204242ef436658d731009e7630bd815e3d52383b0c7085858560405161183f939291906146f4565b60405180910390a36118508a612574565b955050505050509850989650505050505050565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091610736918590610cfd9086612b80565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600080610a0060115460fe611fcd565b6118dd611bff565b6118e5611c1f565b6001600160a01b038281166000908152601560205260409081902080546001600160a01b031916928416929092179091555133907f14298ee83383fc0fa7913c34ea36b3209010c03d0a5a09c606462f80a5f25e589061194890859085906144e7565b60405180910390a2610850611c90565b6000610869601054612b99565b7f000000000000000000000000000000000000000000000000000000000000000081565b600d5490565b6000806109e76011546120b8565b6000610869600e5460ea611c97565b6119b4611c1f565b6119bc612ba2565b604051631f29a8cd60e31b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063f94d466890611a2b907f0000000000000000000000000000000000000000000000000000000000000000906004016145dc565b60006040518083038186803b158015611a4357600080fd5b505afa158015611a57573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a7f91908101906140e1565b50915050611ab681600081518110611a9357fe5b602002602001015182600181518110611aa857fe5b6020026020010151846121fb565b5050336001600160a01b03167f9b6574d1f0a54e6f2d8510751cd8764d42de16aa67970a9bc544cb1028bcdb2683604051611af191906145dc565b60405180910390a2506109d6611c90565b6000806000611b12600f54612cb2565b60125460135491959094509092509050565b6000611b376103ff841115610190611c38565b506103ff811b1992909216911b1790565b6000611b5a6007831115610190611c38565b5060de1b600760de1b19919091161790565b6000611b7e6001841115610190611c38565b6001821b83611b905780198516611b94565b8085175b95945050505050565b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b33600090815260146020526040902054611c1d9060ff166002611c38565b565b611c3160026006541415610190611e31565b6002600655565b816108505762461bcd60e51b6000908152602060045260076024526643464923000030600a808404818106603090810160081b95839006959095019082900491820690940160101b939093010160c81b604452606490fd5b6001600655565b1c6103ff1690565b611ca7613ce1565b600083600760050154108015611cbd5750438411155b611cc75743611cc9565b835b9050611cd3612ba2565b604051631f29a8cd60e31b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063f94d466890611d42907f0000000000000000000000000000000000000000000000000000000000000000906004016145dc565b60006040518083038186803b158015611d5a57600080fd5b505afa158015611d6e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611d9691908101906140e1565b50915050611dcc81600081518110611daa57fe5b602002602001015182600181518110611dbf57fe5b6020026020010151612cb8565b925083611ddd57611ddd8383612dcb565b509250929050565b6000806000611df5600f54612cb2565b905043841415611e0e5764010000000042069150611e27565b640100000000844303600c02420381611e2357fe5b0691505b8103939092509050565b816108505761085081612e6b565b6001600160a01b038316600090815260208190526040902054611e6782821015610196611e31565b611e7e6001600160a01b0384161515610199611e31565b6001600160a01b03808516600090815260208190526040808220858503905591851681522054611eae9083612b80565b6001600160a01b038085166000818152602081815260409182902094909455805186815290519193928816927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a350505050565b611c1d7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015611f6557600080fd5b505afa158015611f79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9d9190613f4c565b6001600160a01b0316336001600160a01b0316146001611c38565b6001600160701b03607082901c811692911690565b1c60011690565b33600090815260156020526040902054611c1d906001600160a01b031615156003611c38565b60007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612067612ebe565b3060405160200180868152602001858152602001848152602001838152602001826001600160a01b031681526020019550505050505060405160208183030381529060405280519060200120905090565b6001600160601b03606082901c811692911690565b60006120dd838311156001611e31565b50900390565b6121227f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b0316146004611c38565b6108507f0000000000000000000000000000000000000000000000000000000000000000821460ce611c38565b6016546000908190612170906001600160a01b038581169116146007611c38565b60115461218b60006121838360fc611fcd565b1460e0611c38565b612196601054612ec2565b601092909255935091506121ad81600160fc611b6c565b6011556040516001600160a01b038516907f5c170c563ea37bd3580cc3b1c6d0d9e35c29182d2e26ce550917bdb7ef34bbcd906121ed90869086906146e6565b60405180910390a250915091565b600080826007600501541080156122125750438311155b61221a574392505b60115460006122298787612cb8565b9050612233611346565b6122ce576122418186612ef3565b6080810151156122745761225882600060fc611b6c565b91506122706010548260e00151836101000151612fee565b6010555b612287828260a001518360c00151612fee565b6011819055506122a4600e5482610120015183610140015161309c565b600e819055506122c1600f54826101600151836101800151613102565b600f556122ce818661317b565b80516020909101519097909650945050505050565b60008060006122f0610854565b90506122fc87876131d2565b6123108161230a878961326d565b90613291565b92506123208161230a868961326d565b9150866001600160a01b03167f998379a72dc64811ce41782d18dd7759dd22de4dd43049986376fada953be82987858560405161235f939291906146f4565b60405180910390a25094509492505050565b6000848152600b6020819052604082206001810154839261239d916001600160a01b0316151590611c38565b60018101546001600160a01b038781169116146123d25760018101546123d2906001600160a01b03878116911614600a611c38565b600181015461241e906001600160a01b0388811691161480612403575060028201546001600160a01b038881169116145b801561241757506001600160a01b03871615155b6008611c38565b600080600061242d84886132b1565b9250925092508261245e577f0000000000000000000000000000000000000000000000000000000000000000612480565b7f00000000000000000000000000000000000000000000000000000000000000005b6001600160a01b0316876124955760006124e2565b836124c0577f00000000000000000000000000000000000000000000000000000000000000006124e2565b7f00000000000000000000000000000000000000000000000000000000000000005b6001600160a01b03168560010160009054906101000a90046001600160a01b03166001600160a01b03167f4ad2915cd8cef57cf8d82983f8bf82308eb03cc31daaa0945f04baf7f2b2611585858f8f604051612541949392919061470a565b60405180910390a4826125545780612556565b815b9550826125635781612565565b805b94505050505094509492505050565b601154606090670de0b6b3a764000083116125b057600061259482612a06565b90508381146125aa576125a782856134c5565b91505b506125e8565b7fc527124565765c9f70b694614157f3cbebdb44c48483db5817d4cc5fa55e6926836040516125df91906145dc565b60405180910390a15b604080516002808252606082018352909160208301908036833701905050915061261181612ec2565b601160008660008151811061262257fe5b602002602001018760018151811061263657fe5b6020908102919091010193909352509190525550919050565b611c1d61265a611346565b156064611c38565b60006126977f000000000000000000000000000000000000000000000000000000000000000062ffffff1683111560df611c38565b60007f000000000000000000000000000000000000000000000000000000000000000061ffff1643816126c657fe5b0643908103915061ffff7f00000000000000000000000000000000000000000000000000000000000000001660018501028201908103600081878161270757fe5b0490506127186000821160d0611c38565b60078054612741908a61272c57600061272e565b835b8b612739578461273c565b60005b613102565b81556000848152600282016020526040902054612764908a61272c57600061272e565b81600201600086815260200190815260200160002081905550600760060160008154809291906001019190505595506040518060c001604052808a15158152602001836001600160701b031681526020016127c383600101548c61350b565b6001600160801b031681526020018c6001600160a01b031681526020018b6001600160a01b03168d6001600160a01b03161415612801576000612803565b8b5b6001600160a01b03908116825260209182018790526000898152600b835260408082208551815487870151888501516001600160801b0316600160781b027fff00000000000000000000000000000000ffffffffffffffffffffffffffffff6001600160701b03909216610100026effffffffffffffffffffffffffff001994151560ff19909416939093179390931691909117161781556060860151600180830180549287166001600160a01b03199384161790556080880151600284018054918816919093161790915560a090960151600390910155918f168152601783529081208054938401815581522001869055600e5482840290612919908b61290c57600061290e565b825b8c612739578361273c565b600e5550949a9950505050505050505050565b600080612949600e54876129415760ea612944565b60f45b611c97565b9050600061295c868302620186a0613291565b601154909150600061296f8260fd611fcd565b905080156129c757600061298283612a06565b9050600061299a858302670de0b6b3a7640000613291565b90506129c1848d6129ac5760006129ae565b825b8e6129b957836129bc565b60005b612fee565b60115550505b8288036000818c6129d857886129da565b895b0190506129f681838e6129ed578b6129ef565b8a5b0290613291565b9c9b505050505050505050505050565b60c01c670fffffffffffffff1690565b6000612a20611ffa565b82604051602001808061190160f01b81525060020183815260200182815260200192505050604051602081830303815290604052805190602001209050919050565b600082118015612a725750600081115b15612ab45760008183870281612a8457fe5b04905060008383890281612a9457fe5b049050612ab1868310158015612aaa5750858210155b60e4611c38565b50505b505050505050565b6000612ad46001600160a01b038716151560e2611c38565b6000612ade610854565b905080612b3757612b016103e887118015612afa57506103e886115b60cc611c38565b6103e8612b0f868802613529565b039150612b1e600f544261357a565b600f55612b2e60006103e86135b8565b43600c55612b5e565b612b5b612b488561230a898561326d565b612b568561230a898661326d565b613649565b91505b612b6c6000831160cc611c38565b612b7687836135b8565b5095945050505050565b6000828201612b928482101583611e31565b9392505050565b60de1c60071690565b604080516001808252818301909252600091816020015b612bc1613d8c565b815260200190600190039081612bb9579050509050600181600081518110612be557fe5b6020026020010151600001906003811115612bfc57fe5b90816003811115612c0957fe5b815250503081600081518110612c1b57fe5b60209081029190910101516001600160a01b039182166060909101526040516303a38fa160e21b81527f000000000000000000000000000000000000000000000000000000000000000090911690630e8e3e8490612c7d908490600401614501565b600060405180830381600087803b158015612c9757600080fd5b505af1158015612cab573d6000803e3d6000fd5b5050505050565b60e01c90565b612cc0613ce1565b600080612ccd858561365f565b60115491935091506000612ce28260fe611fcd565b90506000612cf18360fd611fcd565b90506000612cfe84612a06565b9050600082612d1557670de0b6b3a7640000612d21565b81670de0b6b3a7640000035b90506000808515612d4c57612d37601054612b99565b91508160020a6001018381612d4857fe5b0490505b604051806101e001604052808a81526020018981526020018481526020018281526020018381526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815250995050505050505050505092915050565b6000612dda600e5460e0611c97565b9050600080612de76136f7565b815191935091505b84821015612e4957612e0386838587613772565b612e0e86828461386c565b508151612e1b82846138d8565b7f000000000000000000000000000000000000000000000000000000000000000061ffff1682019150612def565b82518514612ab457849150612e6086838587613772565b612ab486828761386c565b62461bcd60e51b6000908152602060045260076024526642414c23000030600a808404818106603090810160081b95839006959095019082900491820690940160101b939093010160c81b604452606490fd5b4690565b77ffffffffffffffffffffffffffffffffffffffffffffffff198116916001600160601b03606083901c8116921690565b6000612f02600e5460e0611c97565b9050600080612f0f6136f7565b815191935091505b84821015612f7b57612f2b86838587613772565b612f3686828461386c565b508151612f4382846138d8565b612f4d8383613946565b7f000000000000000000000000000000000000000000000000000000000000000061ffff1682019150612f17565b82518514612fb157849150612f9286838587613772565b612f9d86828761386c565b612fa782846138d8565b612fb18383613946565b8251600c5560608301516080840151612fcc9160009161396b565b60075560208301516040840151612fe391906139b2565b600855505050505050565b60006001600160601b03606085901c811690851684156130395761301b8583600019031015610191611c38565b908401906001600160601b03821115613039576001600160601b0391505b831561306d576130528482600019031015610191611c38565b83016001600160601b0381111561306d57506001600160601b035b77ffffffffffffffffffffffffffffffffffffffffffffffff199590951660609190911b179093179392505050565b60006001600160701b03607085901c811690851684156130cb576130c585831015610192611c38565b84820391505b83156130e4576130e084821015610192611c38565b8390035b6001600160e01b03199590951660709190911b179093179392505050565b60006001600160701b03607085901c8116908516841561313b5761313685836001600160701b038016031015610191611c38565b908401905b83156130e45761315b84826001600160701b038016031015610191611c38565b83016001600160e01b03199590951660709190911b179093179392505050565b60008061318783611de5565b909250905063ffffffff8216156131b7576101a08401516012805490910190556101c08401516013805490910190555b6131c9600f548263ffffffff1661357a565b600f5550505050565b6001600160a01b0382166000908152602081905260409020546131fa82821015610196611e31565b6001600160a01b0383166000908152602081905260409020828203905560025461322490836120cd565b6002556040805183815290516000916001600160a01b038616917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a3505050565b6000828202612b9284158061328a57508385838161328757fe5b04145b6003611e31565b60006132a08215156004611e31565b8183816132a957fe5b049392505050565b8154600383015460ff821691600091829161010090046001600160701b031685156132f057600c546132e690831160e3611c38565b600c548203810293505b60006133248715801561330257508343115b61330e5760085461331e565b6000848152600a60205260409020545b8761350b565b8854909150613346908290600160781b90046001600160801b031684896139eb565b9350861580156133565750824311155b156133935787547fff00000000000000000000000000000000ffffffffffffffffffffffffffffff16600160781b6001600160801b038316021788555b6133ac60008611806133a55750600085115b60d4611c38565b861561343457600780546133db90886133c65760006133c8565b845b896133d357856133d6565b60005b61309c565b815560008481526002820160205260409020546133fe90886133c65760006133c8565b6000858152600283016020526040902055600e5461342f9088613422576000613424565b875b896133d357886133d6565b600e55505b61345a600f54876134455785613448565b60005b886134545760006133d6565b8661309c565b600f55868061346857508243115b156134bb5787547fff000000000000000000000000000000000000000000000000000000000000001688556001880180546001600160a01b03199081169091556002890180549091169055600060038901555b5050509250925092565b60006134de670fffffffffffffff831115610190611c38565b5060c01b7ff000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff919091161790565b600081613521576001600160801b038316612b92565b505060801c90565b6000600382111561356c575080600160028204015b818110156135665780915060028182858161355557fe5b04018161355e57fe5b04905061353e565b50610d26565b8115610d2657506001919050565b600061358f63ffffffff831115610190611c38565b5060e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff919091161790565b6001600160a01b0382166000908152602081905260409020546135db9082612b80565b6001600160a01b0383166000908152602081905260409020556002546136019082612b80565b6002556040805182815290516001600160a01b038416916000917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a35050565b60008183106136585781612b92565b5090919050565b600080600080613670600e54611fb8565b91509150600080613682600f54611fb8565b6011549193509150600080613696836120b8565b9150915060008060006136aa8660fc611fcd565b9050806136c2576136bc6010546120b8565b90935091505b6136d28e8b8a01870185016120cd565b9b506136e48d8a8901860184016120cd565b9a50505050505050505050509250929050565b6136ff613dbc565b600c5460009061ffff7f00000000000000000000000000000000000000000000000000000000000000001680828161373357fe5b838652600754919006830391909101925061374d90611fb8565b6080850152606084015260085461376390613a5b565b60408501526020840152509091565b815160608301516080840151918503918183026000613796868302620186a0613a6f565b905082850260006137ac888302620186a0613a6f565b90506000806137bc8d8685613aa2565b915091506000806137d28f888a03878903613ba2565b91509150878f610120018181510191508181525050858f6101400181815101915081815250506138106001600160701b038016831115610191611c38565b6138266001600160701b03821115610191611c38565b6101608f018051830190526101808f018051820190528e5184018f5260208f018051840190526138598d83838d8d613c45565b5050509990985250505050505050505050565b8251600c8383030290156138a1578351602085015182919060701b8161388e57fe5b0402846101a00181815101915081815250505b6020840151156138d2576020840151845182919060701b816138bf57fe5b0402846101c00181815101915081815250505b50505050565b60008281526009602052604081205481906138f290611fb8565b9092509050811561391c576139108284606001511015610192611c38565b60608301805183900390525b80156138d2576139358184608001511015610192611c38565b608083018051829003905250505050565b613958826020015183604001516139b2565b6000918252600a60205260409091205550565b60006139836001600160701b03841115610190611c38565b6139996001600160701b03831115610190611c38565b506001600160e01b03199290921660709190911b171790565b60006139ca6001600160801b03841115610190611c38565b6139e06001600160801b03831115610190611c38565b5060809190911b1790565b60006001600160801b038486031682613a24577f0000000000000000000000000000000000000000000000000000000000000000613a46565b7f00000000000000000000000000000000000000000000000000000000000000005b84820281613a5057fe5b049695505050505050565b608081901c916001600160801b0390911690565b6000613a7e8215156004611e31565b82613a8b5750600061073a565b816001840381613a9757fe5b04600101905061073a565b60808301516040840151600091829181613b1057613aca868202670de0b6b3a7640000613291565b9350613ae0858202670de0b6b3a7640000613291565b9250670de0b6b3a7640000811015613b0b5760a08701805185880301905260c0870180518487030190525b613b98565b60608701516000613b2b888302670de0b6b3a7640000613291565b90506000613b43888402670de0b6b3a7640000613291565b82861b975080861b96509050670de0b6b3a7640000841015613b7e5760a08a018051888b0384900301905260c08a018051878a038390030190525b60e08a018051909201909152610100890180519091019052505b5050935093915050565b60008083151580613bb257508215155b15613c3d578451602086015185613bdd5784018082860281613bd057fe5b0493508382039150613c34565b84613bfc57908501908181870281613bf157fe5b049250829003613c34565b81860181860160008184840281613c0f57fe5b04905060008383870281613c1f57fe5b94839003985090930491829003955093509150505b90865260208601525b935093915050565b8115613c9057817f0000000000000000000000000000000000000000000000000000000000000000840281613c7657fe5b048560200151016001600160801b03168560200181815250505b8015612cab57807f0000000000000000000000000000000000000000000000000000000000000000850281613cc157fe5b048560400151016001600160801b03168560400181815250505050505050565b604051806101e001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b6040805160a081019091528060008152600060208201819052604082018190526060820181905260809091015290565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b8035610d26816147f1565b600082601f830112613e06578081fd5b81356020613e1b613e16836147c9565b6147a5565b8281528181019085830183850287018401881015613e37578586fd5b855b85811015613e5557813584529284019290840190600101613e39565b5090979650505050505050565b600082601f830112613e72578081fd5b81516020613e82613e16836147c9565b8281528181019085830183850287018401881015613e9e578586fd5b855b85811015613e5557815184529284019290840190600101613ea0565b600082601f830112613ecc578081fd5b813567ffffffffffffffff811115613ee057fe5b613ef3601f8201601f19166020016147a5565b818152846020838601011115613f07578283fd5b816020850160208301379081016020019190915292915050565b803560028110610d2657600080fd5b600060208284031215613f41578081fd5b8135612b92816147f1565b600060208284031215613f5d578081fd5b8151612b92816147f1565b60008060408385031215613f7a578081fd5b8235613f85816147f1565b91506020830135613f95816147f1565b809150509250929050565b600080600060608486031215613fb4578081fd5b8335613fbf816147f1565b92506020840135613fcf816147f1565b929592945050506040919091013590565b600080600080600080600060e0888a031215613ffa578485fd5b8735614005816147f1565b96506020880135614015816147f1565b95506040880135945060608801359350608088013560ff81168114614038578384fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215614067578182fd5b8235614072816147f1565b91506020830135613f9581614806565b60008060408385031215614094578182fd5b823561409f816147f1565b946020939093013593505050565b6000806000606084860312156140c1578081fd5b83356140cc816147f1565b95602085013595506040909401359392505050565b6000806000606084860312156140f5578081fd5b835167ffffffffffffffff8082111561410c578283fd5b818601915086601f83011261411f578283fd5b8151602061412f613e16836147c9565b82815281810190858301838502870184018c101561414b578788fd5b8796505b84871015614176578051614162816147f1565b83526001969096019591830191830161414f565b509189015191975090935050508082111561418f578283fd5b5061419c86828701613e62565b925050604084015190509250925092565b6000602082840312156141be578081fd5b8135612b9281614806565b6000602082840312156141da578081fd5b8151612b9281614806565b60008060008060008060008060e0898b031215614200578182fd5b883597506020890135614212816147f1565b96506040890135614222816147f1565b9550606089013567ffffffffffffffff8082111561423e578384fd5b61424a8c838d01613df6565b965060808b0135955060a08b0135945060c08b013591508082111561426d578384fd5b818b0191508b601f830112614280578384fd5b81358181111561428e578485fd5b8c602082850101111561429f578485fd5b6020830194508093505050509295985092959890939650565b6000806000606084860312156142cc578081fd5b833567ffffffffffffffff808211156142e3578283fd5b81860191506101208083890312156142f9578384fd5b614302816147a5565b905061430d83613f21565b815261431b60208401613deb565b602082015261432c60408401613deb565b6040820152606083013560608201526080830135608082015260a083013560a082015261435b60c08401613deb565b60c082015261436c60e08401613deb565b60e08201526101008084013583811115614384578586fd5b6143908a828701613ebc565b9183019190915250976020870135975060409096013595945050505050565b6000602082840312156143c0578081fd5b5035919050565b6000806000606084860312156143db578081fd5b83359250602084013567ffffffffffffffff808211156143f9578283fd5b61440587838801613df6565b9350604086013591508082111561441a578283fd5b5061442786828701613df6565b9150509250925092565b60008060408385031215614443578182fd5b823591506020830135613f9581614806565b60008060408385031215614467578182fd5b50508035926020909101359150565b60008060408385031215614488578182fd5b505080516020909101519092909150565b6000815180845260208085019450808401835b838110156144c8578151875295820195908201906001016144ac565b509495945050505050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b602080825282518282018190526000919060409081850190868401855b8281101561457a57815180516004811061453457fe5b8552808701516001600160a01b03908116888701528682015187870152606080830151821690870152608091820151169085015260a0909301929085019060010161451e565b5091979650505050505050565b60006040825261459a6040830185614499565b8281036020840152611b948185614499565b6000606082526145bf6060830186614499565b60208301949094525060400152919050565b901515815260200190565b90815260200190565b60408101600284106145f357fe5b92815290151560209091015290565b6040810161460f846147e7565b9281526020015290565b60208101614626836147e7565b91905290565b6000602080835283518082850152825b818110156146585785810183015185820160400152820161463c565b818111156146695783604083870101525b50601f01601f1916929092016040019392505050565b600060c0820190508251151582526001600160701b0360208401511660208301526001600160801b03604084015116604083015260608301516001600160a01b038082166060850152806080860151166080850152505060a083015160a083015292915050565b918252602082015260400190565b9283526020830191909152604082015260600190565b938452602084019290925260408301526001600160a01b0316606082015260800190565b93845260208401929092526040830152606082015260800190565b9a8b5260208b019990995260408a01979097526060890195909552608088019390935260a087019190915260c086015260e08501526101008401526101208301526101408201526101600190565b60ff91909116815260200190565b60405181810167ffffffffffffffff811182821017156147c157fe5b604052919050565b600067ffffffffffffffff8211156147dd57fe5b5060209081020190565b600381106109d657fe5b6001600160a01b03811681146109d657600080fd5b80151581146109d657600080fdfea2646970667358221220e5d32adddd65b1ba95d5b33b5251a34884a7f14f2bef5dd0fca9d439a916328864736f6c63430007060033a26469706673582212200199b0c67f83d69c78a57b9bde89d3296c0157a2da8e8cf19ca932350f91512264736f6c63430007060033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8
-----Decoded View---------------
Arg [0] : _vault (address): 0xBA12222222228d8Ba445958a75a0704d566BF2C8
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.