More Info
Private Name Tags
ContractCreator
Loading...
Loading
Contract Name:
Pools
Compiler Version
v0.8.22+commit.4fc1097e
Optimization Enabled:
Yes with 10000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; import "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; import "openzeppelin-contracts/contracts/security/ReentrancyGuard.sol"; import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import "openzeppelin-contracts/contracts/access/Ownable.sol"; import "../interfaces/IExchangeConfig.sol"; import "../arbitrage/ArbitrageSearch.sol"; import "./interfaces/IPoolsConfig.sol"; import "./interfaces/IPools.sol"; import "./PoolStats.sol"; import "./PoolUtils.sol"; // The Pools contract stores the reserves that are used for swaps within the DEX. // It handles deposits, arbitrage, and keeps stats for proportional rewards distribution to the liquidity providers. // // Only the Liquidity contract can actually call addLiquidity and removeLiquidity. // User liquidity accounting is done by Liquidity (via its derivation of StakingRewards). contract Pools is IPools, ReentrancyGuard, PoolStats, ArbitrageSearch, Ownable { event LiquidityAdded(IERC20 indexed tokenA, IERC20 indexed tokenB, uint256 addedAmountA, uint256 addedAmountB, uint256 addedLiquidity); event LiquidityRemoved(IERC20 indexed tokenA, IERC20 indexed tokenB, uint256 reclaimedA, uint256 reclaimedB, uint256 removedLiquidity); event TokenDeposit(address indexed user, IERC20 indexed token, uint256 amount); event TokenWithdrawal(address indexed user, IERC20 indexed token, uint256 amount); event SwapAndArbitrage(address indexed user, IERC20 indexed swapTokenIn, IERC20 indexed swapTokenOut, uint256 swapAmountIn, uint256 swapAmountOut, uint256 arbitrageProfit); using SafeERC20 for IERC20; struct PoolReserves { uint128 reserve0; // The token reserves such that address(token0) < address(token1) uint128 reserve1; } IDAO public dao; ILiquidity public liquidity; // Set to true when starting the exchange is approved by the bootstrapBallot bool public exchangeIsLive; // Keeps track of the pool reserves by poolID mapping(bytes32=>PoolReserves) private _poolReserves; // User token balances for deposited tokens mapping(address=>mapping(IERC20=>uint256)) private _userDeposits; // Used to prevent splitting large swaps within a single block into smaller ones as doing so allows for greater price manipulation without consequence from the arbitrage rebalancing. mapping(address => uint) private lastSwappedBlocks; constructor( IExchangeConfig _exchangeConfig, IPoolsConfig _poolsConfig ) ArbitrageSearch(_exchangeConfig) PoolStats(_exchangeConfig, _poolsConfig) { zero = _exchangeConfig.zero(); } modifier ensureNotExpired(uint256 deadline) { require(block.timestamp <= deadline, "TX EXPIRED"); _; } // This will be called only once - at deployment time function setContracts( IDAO _dao, ILiquidity _liquidity ) external onlyOwner { dao = _dao; liquidity = _liquidity; // setContracts can only be called once renounceOwnership(); } function startExchangeApproved() external nonReentrant { require( msg.sender == address(exchangeConfig.initialDistribution().bootstrapBallot()), "Pools.startExchangeApproved can only be called from the BootstrapBallot contract" ); // Make sure that the arbitrage indicies for the whitelisted pools are updated before starting the exchange updateArbitrageIndicies(); exchangeIsLive = true; } // Add the given amount of two tokens to the specified liquidity pool. // The maximum amount of tokens is added while having the added amount have the same ratio as the current reserves. function _addLiquidity( bytes32 poolID, uint256 maxAmount0, uint256 maxAmount1, uint256 totalLiquidity ) internal returns(uint256 addedAmount0, uint256 addedAmount1, uint256 addedLiquidity) { PoolReserves storage reserves = _poolReserves[poolID]; uint256 reserve0 = reserves.reserve0; uint256 reserve1 = reserves.reserve1; // If either reserve is zero then consider the pool to be empty and that the added liquidity will become the initial token ratio if ( ( reserve0 == 0 ) || ( reserve1 == 0 ) ) { // Update the reserves reserves.reserve0 += uint128(maxAmount0); reserves.reserve1 += uint128(maxAmount1); // Default liquidity will be the addition of both maxAmounts in case one of them is much smaller (has smaller decimals) return ( maxAmount0, maxAmount1, (maxAmount0 + maxAmount1) ); } // Add liquidity to the pool proportional to the current existing token reserves in the pool. // First, try the proportional amount of tokenB for the given maxAmountA uint256 proportionalB = ( maxAmount0 * reserve1 ) / reserve0; // proportionalB too large for the specified maxAmountB? if ( proportionalB > maxAmount1 ) { // Use maxAmountB and a proportional amount for tokenA instead addedAmount0 = ( maxAmount1 * reserve0 ) / reserve1; addedAmount1 = maxAmount1; } else { addedAmount0 = maxAmount0; addedAmount1 = proportionalB; } // Ensure that the added amounts are at least DUST require( addedAmount0 > PoolUtils.DUST, "Added liquidity for token 0 less than DUST" ); require( addedAmount1 > PoolUtils.DUST, "Added liquidity for token 1 less than DUST" ); // Update the reserves reserves.reserve0 += uint128(addedAmount0); reserves.reserve1 += uint128(addedAmount1); // Determine the amount of liquidity that will be given to the user to reflect their share of the total liquidity. addedLiquidity = (totalLiquidity * (addedAmount0+addedAmount1) ) / (reserve0+reserve1); } // Add liquidity to the specified pool (must be a whitelisted pool) // Only callable from the Liquidity contract - so it can specify totalLiquidity with authority function addLiquidity( IERC20 tokenA, IERC20 tokenB, uint256 maxAmountA, uint256 maxAmountB, uint256 minAddedAmountA, uint256 minAddedAmountB, uint256 totalLiquidity ) external nonReentrant returns (uint256 addedAmountA, uint256 addedAmountB, uint256 addedLiquidity) { require( msg.sender == address(liquidity), "Pools.addLiquidity is only callable from the Liquidity contract" ); require( exchangeIsLive, "The exchange is not yet live" ); require( address(tokenA) != address(tokenB), "Cannot add liquidity for duplicate tokens" ); require( maxAmountA > PoolUtils.DUST, "The amount of tokenA to add is too small" ); require( maxAmountB > PoolUtils.DUST, "The amount of tokenB to add is too small" ); (bytes32 poolID, bool flipped) = PoolUtils._poolIDAndFlipped(tokenA, tokenB); // Flip the users arguments if they are not in reserve token order with address(tokenA) < address(tokenB) if ( flipped ) (addedAmountB, addedAmountA, addedLiquidity) = _addLiquidity( poolID, maxAmountB, maxAmountA, totalLiquidity ); else (addedAmountA, addedAmountB, addedLiquidity) = _addLiquidity( poolID, maxAmountA, maxAmountB, totalLiquidity ); // Make sure the minimum liquidity has been added require( addedAmountA >= minAddedAmountA, "Insufficient tokenA added to liquidity" ); require( addedAmountB >= minAddedAmountB, "Insufficient tokenB added to liquidity" ); // Transfer the tokens from the sender - only tokens without fees should be whitelisted on the DEX tokenA.safeTransferFrom(msg.sender, address(this), addedAmountA ); tokenB.safeTransferFrom(msg.sender, address(this), addedAmountB ); emit LiquidityAdded(tokenA, tokenB, addedAmountA, addedAmountB, addedLiquidity); } // Remove liquidity for the user and reclaim the underlying tokens // Only callable from the Liquidity contract - so it can specify totalLiquidity with authority function removeLiquidity( IERC20 tokenA, IERC20 tokenB, uint256 liquidityToRemove, uint256 minReclaimedA, uint256 minReclaimedB, uint256 totalLiquidity ) external nonReentrant returns (uint256 reclaimedA, uint256 reclaimedB) { require( msg.sender == address(liquidity), "Pools.removeLiquidity is only callable from the Liquidity contract" ); require( liquidityToRemove > 0, "The amount of liquidityToRemove cannot be zero" ); (bytes32 poolID, bool flipped) = PoolUtils._poolIDAndFlipped(tokenA, tokenB); // Determine how much liquidity is being withdrawn and round down in favor of the protocol PoolReserves storage reserves = _poolReserves[poolID]; if (reserves.reserve0 <= reserves.reserve1 ) { reclaimedA = ( reserves.reserve0 * liquidityToRemove ) / totalLiquidity; reclaimedB = ( reserves.reserve1 * reclaimedA ) / reserves.reserve0; } else { reclaimedB = ( reserves.reserve1 * liquidityToRemove ) / totalLiquidity; reclaimedA = ( reserves.reserve0 * reclaimedB ) / reserves.reserve1; } reserves.reserve0 -= uint128(reclaimedA); reserves.reserve1 -= uint128(reclaimedB); // Make sure that removing liquidity doesn't drive either of the reserves below DUST. // This is to ensure that ratios remain relatively constant even after a maximum withdrawal. require((reserves.reserve0 >= PoolUtils.DUST) && (reserves.reserve1 >= PoolUtils.DUST), "Insufficient reserves after liquidity removal"); // Switch reclaimed amounts back to the order that was specified in the call arguments so they make sense to the caller if (flipped) (reclaimedA,reclaimedB) = (reclaimedB,reclaimedA); require( (reclaimedA >= minReclaimedA) && (reclaimedB >= minReclaimedB), "Insufficient underlying tokens returned" ); // Send the reclaimed tokens to the user tokenA.safeTransfer( msg.sender, reclaimedA ); tokenB.safeTransfer( msg.sender, reclaimedB ); emit LiquidityRemoved(tokenA, tokenB, reclaimedA, reclaimedB, liquidityToRemove); } // Allow users to deposit tokens into the contract. // This is not rewarded or considered staking in any way. It's simply a way to reduce gas costs by preventing transfers at swap time. function deposit( IERC20 token, uint256 amount ) external nonReentrant { require( amount > PoolUtils.DUST, "Deposit amount too small"); _userDeposits[msg.sender][token] += amount; // Transfer the tokens from the sender - only tokens without fees should be whitelisted on the DEX token.safeTransferFrom(msg.sender, address(this), amount ); emit TokenDeposit(msg.sender, token, amount); } // Withdraw tokens that were previously deposited function withdraw( IERC20 token, uint256 amount ) external nonReentrant { require( _userDeposits[msg.sender][token] >= amount, "Insufficient balance to withdraw specified amount" ); require( amount > PoolUtils.DUST, "Withdraw amount too small"); _userDeposits[msg.sender][token] -= amount; // Send the token to the user token.safeTransfer( msg.sender, amount ); emit TokenWithdrawal(msg.sender, token, amount); } // Swap amountIn tokens for amountOut tokens in the direction specified by flipped and update the reserves. // Only the reserves are updated - the function does not adjust deposited user balances or do ERC20 transfers. // Assumes that the reserves have already been checked for minimal necessary liquidity. function _adjustReservesForSwap( PoolReserves storage reserves, bool flipped, uint256 amountIn ) internal returns (uint256 amountOut) { // Constant Product AMM Math // k=r0*r1 • product of reserves is constant k // k=(r0+amountIn)*(r1-amountOut) • add some token0 to r0 and swap it for some token1 which is removed from r1 // r1-amountOut=k/(r0+amountIn) • divide by (r0+amountIn) and flip // amountOut=r1-k/(r0+amountIn) • multiply by -1 and isolate amountOut // amountOut(r0+amountIn)=r1(r0+amountIn)-k • multiply by (r0+amountIn) // amountOut(r0+amountIn)=r1*r0+r1*amountIn-k • multiply r1 by (r0+amountIn) // amountOut(r0+amountIn)=k+r1*amountIn-k • r0*r1=k (from above) // amountOut(r0+amountIn)=r1*amountIn • cancel k // amountOut=r1*amountIn/(r0+amountIn) • isolate amountOut uint256 reserve0 = reserves.reserve0; uint256 reserve1 = reserves.reserve1; // See if the reserves should be flipped if (flipped) { reserve1 += amountIn; amountOut = reserve0 * amountIn / reserve1; reserve0 -= amountOut; } else { reserve0 += amountIn; amountOut = reserve1 * amountIn / reserve0; reserve1 -= amountOut; } // Make sure that the reserves after swap are greater than DUST require( (reserve0 > PoolUtils.DUST) && (reserve1 > PoolUtils.DUST), "Insufficient reserves after swap"); // Update the reserves with an overflow check require( (reserve0 <= type(uint128).max) && (reserve1 <= type(uint128).max), "Reserves overflow after swap" ); reserves.reserve0 = uint128(reserve0); reserves.reserve1 = uint128(reserve1); } // Arbitrage a token to itself along a specified circular path (starting and ending with WETH), taking advantage of imbalances in the exchange pools. // Does not require any deposited tokens to make the call, but requires that the resulting amountOut is greater than the specified arbitrageAmountIn. // Essentially the caller virtually "borrows" arbitrageAmountIn of the starting token and virtually "repays" it from their received amountOut at the end of the swap chain. // The extra amountOut (compared to arbitrageAmountIn) is the arbitrageProfit. function _arbitrage(uint256 arbitrageAmountIn, PoolReserves storage reservesA, PoolReserves storage reservesB, PoolReserves storage reservesC, bool flippedA, bool flippedB, bool flippedC ) internal returns (uint256 arbitrageProfit) { uint256 amountOut = _adjustReservesForSwap( reservesA, flippedA, arbitrageAmountIn ); amountOut = _adjustReservesForSwap( reservesB, flippedB, amountOut ); amountOut = _adjustReservesForSwap( reservesC, flippedC, amountOut ); // Will revert if amountOut < arbitrageAmountIn arbitrageProfit = amountOut - arbitrageAmountIn; // Immediately swap the generated WETH arbitrage profits to ZERO (bytes32 poolID, bool flipped) = PoolUtils._poolIDAndFlipped(weth, zero); PoolReserves storage reserves = _poolReserves[poolID]; // Only swap for ZERO with sufficient reserves if ( ( reserves.reserve0 > PoolUtils.DUST ) && ( reserves.reserve1 > PoolUtils.DUST ) ) { uint256 zeroOut = _adjustReservesForSwap(reserves, flipped, arbitrageProfit); // Deposit the swapped ZERO for the DAO - to be used later within DAO.performUpkeep _userDeposits[address(dao)][zero] += zeroOut; } } // Check to see if profitable arbitrage is possible after the user swap that was just made // Check the arbitrage path: WETH->arbToken2->arbToken3->WETH function _attemptArbitrage( IERC20 arbToken2, IERC20 arbToken3 ) internal returns (uint256 arbitrageProfit) { bytes32 poolID; bool flippedA; bool flippedB; bool flippedC; PoolReserves storage reservesA; PoolReserves storage reservesB; PoolReserves storage reservesC; // Given the specified arbitrage path, determine the best arbitrageAmountIn to use uint256 arbitrageAmountIn; { (poolID, flippedA) = PoolUtils._poolIDAndFlipped(weth, arbToken2); reservesA = _poolReserves[poolID]; (uint256 a0, uint256 a1) = (reservesA.reserve0, reservesA.reserve1 ); if (flippedA) (a0, a1) = (a1, a0); (poolID, flippedB) = PoolUtils._poolIDAndFlipped(arbToken2, arbToken3); reservesB = _poolReserves[poolID]; (uint256 b0, uint256 b1) = (reservesB.reserve0, reservesB.reserve1 ); if (flippedB) (b0, b1) = (b1, b0); (poolID, flippedC) = PoolUtils._poolIDAndFlipped(arbToken3, weth); reservesC = _poolReserves[poolID]; (uint256 c0, uint256 c1) = (reservesC.reserve0, reservesC.reserve1 ); if (flippedC) (c0, c1) = (c1, c0); // Determine the best amount of WETH to start the arbitrage with if ( a0 > PoolUtils.DUST && a1 > PoolUtils.DUST && b0 > PoolUtils.DUST && b1 > PoolUtils.DUST && c0 > PoolUtils.DUST && c1 > PoolUtils.DUST ) arbitrageAmountIn = _bestArbitrageIn(a0, a1, b0, b1, c0, c1 ); } // If arbitrage is viable, then perform it if (arbitrageAmountIn > 0) { arbitrageProfit = _arbitrage(arbitrageAmountIn, reservesA, reservesB, reservesC, flippedA, flippedB, flippedC); // Update the stats related to the pools that contributed to the arbitrage so they can be rewarded proportionally later. // The arbitrage path can be identified by the middle tokens arbToken2 and arbToken3 (with WETH always on both ends) _updateProfitsFromArbitrage( arbToken2, arbToken3, arbitrageProfit ); } } // Adjust the reserves for swapping between the two specified tokens and then immediately attempt arbitrage. // Does not require exchange access for the sending wallet. function _adjustReservesForSwapAndAttemptArbitrage( IERC20 swapTokenIn, IERC20 swapTokenOut, uint256 swapAmountIn, uint256 minAmountOut ) internal returns (uint256 swapAmountOut) { // Place the user swap first (bytes32 poolID, bool flipped) = PoolUtils._poolIDAndFlipped(swapTokenIn, swapTokenOut); PoolReserves storage reserves = _poolReserves[poolID]; // Revert if reserves are insufficient require((reserves.reserve0 > PoolUtils.DUST) && (reserves.reserve1 > PoolUtils.DUST), "Insufficient reserves before swap"); swapAmountOut = _adjustReservesForSwap( reserves, flipped, swapAmountIn ); // Make sure the swap meets the minimums specified by the user require( swapAmountOut >= minAmountOut, "Insufficient resulting token amount" ); // The user's swap has just been made - attempt atomic arbitrage to rebalance the pool and yield arbitrage profit. // Determine the arbitrage path for the given user swap. // Arbitrage path returned as: weth->arbToken2->arbToken3->weth (IERC20 arbToken2, IERC20 arbToken3) = _arbitragePath( swapTokenIn, swapTokenOut ); uint256 arbitrageProfit = _attemptArbitrage( arbToken2, arbToken3 ); emit SwapAndArbitrage(msg.sender, swapTokenIn, swapTokenOut, swapAmountIn, swapAmountOut, arbitrageProfit); } // Swap one token for another via a direct whitelisted pool. // Having simpler swaps without multiple tokens in the swap chain makes it simpler (and less expensive gas wise) to find suitable arbitrage opportunities. // Cheap arbitrage gas-wise is important as arbitrage will be atomically attempted with every user swap transaction. // Requires that swapTokenIn has already been deposited for the caller. function swap( IERC20 swapTokenIn, IERC20 swapTokenOut, uint256 swapAmountIn, uint256 minAmountOut, uint256 deadline ) external nonReentrant ensureNotExpired(deadline) returns (uint256 swapAmountOut) { // Confirm and adjust user deposits mapping(IERC20=>uint256) storage userDeposits = _userDeposits[msg.sender]; require( userDeposits[swapTokenIn] >= swapAmountIn, "Insufficient deposited token balance of initial token" ); userDeposits[swapTokenIn] -= swapAmountIn; swapAmountOut = _adjustReservesForSwapAndAttemptArbitrage(swapTokenIn, swapTokenOut, swapAmountIn, minAmountOut ); // Deposit the final tokenOut for the caller userDeposits[swapTokenOut] += swapAmountOut; } // Deposit tokenIn, swap to tokenOut and then have tokenOut sent to the sender function depositSwapWithdraw(IERC20 swapTokenIn, IERC20 swapTokenOut, uint256 swapAmountIn, uint256 minAmountOut, uint256 deadline ) external nonReentrant ensureNotExpired(deadline) returns (uint256 swapAmountOut) { // Transfer the tokens from the sender - only tokens without fees should be whitelisted on the DEX swapTokenIn.safeTransferFrom(msg.sender, address(this), swapAmountIn ); swapAmountOut = _adjustReservesForSwapAndAttemptArbitrage(swapTokenIn, swapTokenOut, swapAmountIn, minAmountOut ); // Send tokenOut to the user swapTokenOut.safeTransfer( msg.sender, swapAmountOut ); } // Deposit tokenIn, swap to tokenOut without arbitrage and then have tokenOut sent to the sender. // Only callable by the Liquidity contract function depositZapSwapWithdraw(IERC20 zapSwapTokenIn, IERC20 zapSwapTokenOut, uint256 zapSwapAmountIn ) external returns (uint256 zapSwapAmountOut) { require( msg.sender == address(liquidity), "Pools.depositZapSwapWithdraw is only callable from the Liquidity contract" ); // Transfer the tokens from the sender - only tokens without fees should be whitelisted on the DEX zapSwapTokenIn.safeTransferFrom(msg.sender, address(this), zapSwapAmountIn ); // Perform the zap swap without arbitrage or minimum checks (as the users final swap will be checked for relevant minimums). // PoolMath.determineZapSwapAmount already checked for reservers > DUST as well. (bytes32 poolID, bool flipped) = PoolUtils._poolIDAndFlipped(zapSwapTokenIn, zapSwapTokenOut); PoolReserves storage reserves = _poolReserves[poolID]; // Prevent users from zapping too much at once as they may encounter unexpected slippage if ( flipped ) require( zapSwapAmountIn < reserves.reserve1 / 100, "Cannot zap more than 1% of the reserves" ); else require( zapSwapAmountIn < reserves.reserve0 / 100, "Cannot zap more than 1% of the reserves" ); zapSwapAmountOut = _adjustReservesForSwap( reserves, flipped, zapSwapAmountIn ); // Send tokenOut to the user zapSwapTokenOut.safeTransfer( msg.sender, zapSwapAmountOut ); } // A convenience method to perform two swaps in one transaction function depositDoubleSwapWithdraw( IERC20 swapTokenIn, IERC20 swapTokenMiddle, IERC20 swapTokenOut, uint256 swapAmountIn, uint256 minAmountOut, uint256 deadline ) external nonReentrant ensureNotExpired(deadline) returns (uint256 swapAmountOut) { swapTokenIn.safeTransferFrom(msg.sender, address(this), swapAmountIn ); uint256 middleAmountOut = _adjustReservesForSwapAndAttemptArbitrage(swapTokenIn, swapTokenMiddle, swapAmountIn, 0 ); swapAmountOut = _adjustReservesForSwapAndAttemptArbitrage(swapTokenMiddle, swapTokenOut, middleAmountOut, minAmountOut ); swapTokenOut.safeTransfer( msg.sender, swapAmountOut ); } // === VIEWS === // The pool reserves for two specified tokens - returned in the order specified by the caller function getPoolReserves(IERC20 tokenA, IERC20 tokenB) public view returns (uint256 reserveA, uint256 reserveB) { (bytes32 poolID, bool flipped) = PoolUtils._poolIDAndFlipped(tokenA, tokenB); PoolReserves memory reserves = _poolReserves[poolID]; reserveA = reserves.reserve0; reserveB = reserves.reserve1; // Return the reserves in the order that they were requested if (flipped) (reserveA, reserveB) = (reserveB, reserveA); } // A user's deposited balance for a token function depositedUserBalance(address user, IERC20 token) public view returns (uint256) { return _userDeposits[user][token]; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value)); } /** * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value)); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, approvalCall); } } /** * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _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 making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == _ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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); /** * @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 `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; import "openzeppelin-contracts/contracts/finance/VestingWallet.sol"; import "../staking/interfaces/ILiquidity.sol"; import "../launch/interfaces/IInitialDistribution.sol"; import "../rewards/interfaces/IRewardsEmitter.sol"; import "../rewards/interfaces/IZeroRewards.sol"; import "../rewards/interfaces/IEmissions.sol"; import "../interfaces/IAccessManager.sol"; import "../launch/interfaces/IAirdrop.sol"; import "../dao/interfaces/IDAO.sol"; import "../interfaces/IZero.sol"; import "./IUpkeep.sol"; interface IExchangeConfig { function setContracts( IDAO _dao, IUpkeep _upkeep, IInitialDistribution _initialDistribution, VestingWallet _teamVestingWallet, VestingWallet _daoVestingWallet ) external; // onlyOwner function setAccessManager( IAccessManager _accessManager ) external; // onlyOwner // Views function zero() external view returns (IZero); function wbtc() external view returns (IERC20); function weth() external view returns (IERC20); function usdc() external view returns (IERC20); function usdt() external view returns (IERC20); function daoVestingWallet() external view returns (VestingWallet); function teamVestingWallet() external view returns (VestingWallet); function initialDistribution() external view returns (IInitialDistribution); function accessManager() external view returns (IAccessManager); function dao() external view returns (IDAO); function upkeep() external view returns (IUpkeep); function teamWallet() external view returns (address); function walletHasAccess( address wallet ) external view returns (bool); }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; import "openzeppelin-contracts/contracts/utils/math/Math.sol"; import "../interfaces/IExchangeConfig.sol"; import "../pools/PoolUtils.sol"; // Finds a circular path after a user's swap has occurred (from WETH to WETH in this case) that results in an arbitrage profit. abstract contract ArbitrageSearch { IERC20 immutable public weth; IERC20 immutable public zero; IERC20 immutable public usdc; constructor( IExchangeConfig _exchangeConfig ) { // Cached for efficiency weth = _exchangeConfig.weth(); zero = _exchangeConfig.zero(); usdc = _exchangeConfig.usdc(); } // Returns the middle two tokens in an arbitrage path that starts and ends with WETH. // The WETH tokens at the beginning and end of the path are not returned as they are always the same. // Full arbitrage cycle is: WETH->arbToken2->arbToken3->WETH function _arbitragePath( IERC20 swapTokenIn, IERC20 swapTokenOut ) internal view returns (IERC20 arbToken2, IERC20 arbToken3) { // swap: ZERO->WETH // arb: WETH->ZERO->USDC->WETH if ( address(swapTokenIn) == address(zero)) if ( address(swapTokenOut) == address(weth)) return (zero, usdc); // swap: WETH->ZERO // arb: WETH->USDC->ZERO->WETH if ( address(swapTokenIn) == address(weth)) if ( address(swapTokenOut) == address(zero)) return (usdc, zero); // swap: WETH->swapTokenOut // arb: WETH->ZERO->swapTokenOut->WETH if ( address(swapTokenIn) == address(weth)) return (zero, swapTokenOut); // swap: swapTokenIn->WETH // arb: WETH->swapTokenIn->ZERO->WETH if ( address(swapTokenOut) == address(weth)) return (swapTokenIn, zero); // swap: swapTokenIn->swapTokenOut // arb: WETH->swapTokenOut->swapTokenIn->WETH return (swapTokenOut, swapTokenIn); } // Determine the most significant bit of a non-zero number function _mostSignificantBit(uint256 x) internal pure returns (uint256 msb) { unchecked { if (x >= 2**128) { x >>= 128; msb += 128; } if (x >= 2**64) { x >>= 64; msb += 64; } if (x >= 2**32) { x >>= 32; msb += 32; } if (x >= 2**16) { x >>= 16; msb += 16; } if (x >= 2**8) { x >>= 8; msb += 8; } if (x >= 2**4) { x >>= 4; msb += 4; } if (x >= 2**2) { x >>= 2; msb += 2; } if (x >= 2**1) { x >>= 1; msb += 1; } } } // Given that x, y and z will be multiplied: determine the bit shift necessary to keep the product contained in 240 bits function _shiftRequired( uint256 x, uint256 y, uint256 z ) internal pure returns (uint256) { unchecked { // Determine the maximum number of bits required without shifting uint256 requiredBits0 = _mostSignificantBit(x) + _mostSignificantBit(y) + _mostSignificantBit(z); // Already fits in 240? if ( requiredBits0 < 240 ) return 0; // Each number will be shifted so we can divide the required difference by 3 return Math.ceilDiv( requiredBits0 - 240, 3 ); } } // Determine the shift required to keep a0 * b0 * c0 and a1 * b1 * c1 within 240 bits function _determineShift( uint256 a0, uint256 b0, uint256 c0, uint256 a1, uint256 b1, uint256 c1 ) internal pure returns (uint256) { uint256 shift0 = _shiftRequired(a0, b0, c0); uint256 shift1 = _shiftRequired(a1, b1, c1); return shift0 > shift1 ? shift0 : shift1; } function _bestArbitrageIn( uint256 a0, uint256 a1, uint256 b0, uint256 b1, uint256 c0, uint256 c1 ) internal pure returns (uint256 bestArbAmountIn) { // This can be unchecked as the actual arbitrage that is performed when this is non-zero is checked and duplicates the check for profitability. // testArbitrageMethodsLarge() checks for proper behavior with extremely large reserves as well. unchecked { // Original derivation: https://github.com/code-423n4/2024-01-salty-findings/issues/419 // uint256 n0 = A0 * B0 * C0; // uint256 n1 = A1 * B1 * C1; // if (n1 <= n0) return 0; // // uint256 m = A1 * B1 + C0 * B0 + C0 * A1; // uint256 z = Math.sqrt(A0 * C1); // z *= Math.sqrt(A1 * B0); // z *= Math.sqrt(B1 * C0); // bestArbAmountIn = (z - n0) / m; // Determine the shift required to keep a0 * b0 * c0 and a1 * b1 * c1 each within 240 bits uint256 shift = _determineShift( a0, b0, c0, a1, b1, c1 ); if ( shift > 0 ) { a0 = a0 >> shift; a1 = a1 >> shift; b0 = b0 >> shift; b1 = b1 >> shift; c0 = c0 >> shift; c1 = c1 >> shift; } // Each variable will use less than 80 bits uint256 n0 = a0 * b0 * c0; uint256 n1 = a1 * b1 * c1; if (n1 <= n0) return 0; uint256 m = a1 * b1 + c0 * ( b0 + a1 ); // Calculating n0 * n1 directly would overflow under some situations. // Multiply the sqrt's instead - effectively keeping the max size the same uint256 z = Math.sqrt(n0) * Math.sqrt(n1); bestArbAmountIn = ( z - n0 ) / m; if ( bestArbAmountIn == 0 ) return 0; // Needed for the below arbitrage profit testing if ( shift > 0 ) { // Convert back to normal scaling bestArbAmountIn = bestArbAmountIn << shift; a0 = a0 << shift; a1 = a1 << shift; b0 = b0 << shift; b1 = b1 << shift; c0 = c0 << shift; c1 = c1 << shift; } // Make sure bestArbAmountIn arbitrage is actually profitable (or else it will revert when actually performed in Pools.sol) uint256 amountOut = (a1 * bestArbAmountIn) / (a0 + bestArbAmountIn); amountOut = (b1 * amountOut) / (b0 + amountOut); amountOut = (c1 * amountOut) / (c0 + amountOut); if ( amountOut < bestArbAmountIn ) return 0; } } }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import "./IPools.sol"; interface IPoolsConfig { function whitelistPool( IERC20 tokenA, IERC20 tokenB ) external; // onlyOwner function unwhitelistPool( IERC20 tokenA, IERC20 tokenB ) external; // onlyOwner function changeMaximumWhitelistedPools(bool increase) external; // onlyOwner // Views function maximumWhitelistedPools() external view returns (uint256); function numberOfWhitelistedPools() external view returns (uint256); function isWhitelisted( bytes32 poolID ) external view returns (bool); function whitelistedPools() external view returns (bytes32[] calldata); function underlyingTokenPair( bytes32 poolID ) external view returns (IERC20 tokenA, IERC20 tokenB); // Returns true if the token has been whitelisted (meaning it has been pooled with either WETH and ZERO) function tokenHasBeenWhitelisted( IERC20 token, IERC20 weth, IERC20 zero ) external view returns (bool); }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; import "../../staking/interfaces/ILiquidity.sol"; import "../../dao/interfaces/IDAO.sol"; import "./IPoolStats.sol"; interface IPools is IPoolStats { function startExchangeApproved() external; function setContracts( IDAO _dao, ILiquidity _liquidity ) external; // onlyOwner function addLiquidity( IERC20 tokenA, IERC20 tokenB, uint256 maxAmountA, uint256 maxAmountB, uint256 minAddedAmountA, uint256 minAddedAmountB, uint256 totalLiquidity ) external returns (uint256 addedAmountA, uint256 addedAmountB, uint256 addedLiquidity); function removeLiquidity( IERC20 tokenA, IERC20 tokenB, uint256 liquidityToRemove, uint256 minReclaimedA, uint256 minReclaimedB, uint256 totalLiquidity ) external returns (uint256 reclaimedA, uint256 reclaimedB); function deposit( IERC20 token, uint256 amount ) external; function withdraw( IERC20 token, uint256 amount ) external; function swap( IERC20 swapTokenIn, IERC20 swapTokenOut, uint256 swapAmountIn, uint256 minAmountOut, uint256 deadline ) external returns (uint256 swapAmountOut); function depositSwapWithdraw(IERC20 swapTokenIn, IERC20 swapTokenOut, uint256 swapAmountIn, uint256 minAmountOut, uint256 deadline ) external returns (uint256 swapAmountOut); function depositDoubleSwapWithdraw( IERC20 swapTokenIn, IERC20 swapTokenMiddle, IERC20 swapTokenOut, uint256 swapAmountIn, uint256 minAmountOut, uint256 deadline ) external returns (uint256 swapAmountOut); function depositZapSwapWithdraw(IERC20 swapTokenIn, IERC20 swapTokenOut, uint256 swapAmountIn ) external returns (uint256 swapAmountOut); // Views function exchangeIsLive() external view returns (bool); function getPoolReserves(IERC20 tokenA, IERC20 tokenB) external view returns (uint256 reserveA, uint256 reserveB); function depositedUserBalance(address user, IERC20 token) external view returns (uint256); }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import "../interfaces/IExchangeConfig.sol"; import "./interfaces/IPoolsConfig.sol"; import "./interfaces/IPoolStats.sol"; import "./PoolUtils.sol"; // Keeps track of the arbitrage profits generated by pools (for rewards distribution proportional to the profits generated per pool). abstract contract PoolStats is IPoolStats { uint64 constant INVALID_POOL_ID = type(uint64).max; IExchangeConfig immutable public exchangeConfig; IPoolsConfig immutable public poolsConfig; IERC20 immutable public _weth; // poolID(arbToken2, arbToken3) => arbitrage profits contributed since the last performUpkeep mapping(bytes32=>uint256) public _arbitrageProfits; // Maps poolID(arbToken2, arbToken3) => the indicies (within the whitelistedPools array) of the pools involved in WETH->arbToken2->arbToken3->WETH mapping(bytes32=>ArbitrageIndicies) public _arbitrageIndicies; constructor( IExchangeConfig _exchangeConfig, IPoolsConfig _poolsConfig ) { exchangeConfig = _exchangeConfig; poolsConfig = _poolsConfig; _weth = exchangeConfig.weth(); } // Record that arbitrageProfit was generated and the a specific arbitrage path generated it (which is defined by the middle two tokens in WETH->arbToken2->arbToken3->WETH) function _updateProfitsFromArbitrage( IERC20 arbToken2, IERC20 arbToken3, uint256 arbitrageProfit ) internal { // Though three pools contributed to the arbitrage we can record just the middle one as we know the input and output token will be WETH bytes32 poolID = PoolUtils._poolID( arbToken2, arbToken3 ); _arbitrageProfits[poolID] += arbitrageProfit; } // Called at the end of Upkeep.performUpkeep to reset the arbitrage stats for the pools function clearProfitsForPools() external { require(msg.sender == address(exchangeConfig.upkeep()), "PoolStats.clearProfitsForPools is only callable from the Upkeep contract" ); bytes32[] memory poolIDs = poolsConfig.whitelistedPools(); // Don't fully set profits to zero to avoid the increased gas cost of overwriting zero for( uint256 i = 0; i < poolIDs.length; i++ ) _arbitrageProfits[ poolIDs[i] ] = 1; } // The index of pool tokenA/tokenB within the whitelistedPools array. // Should always find a value as only whitelisted pools are used in the arbitrage path. // Returns uint64.max in the event of failed lookup function _poolIndex( IERC20 tokenA, IERC20 tokenB, bytes32[] memory poolIDs ) internal pure returns (uint64 index) { bytes32 poolID = PoolUtils._poolID( tokenA, tokenB ); for( uint256 i = 0; i < poolIDs.length; i++ ) { if (poolID == poolIDs[i]) return uint64(i); } return INVALID_POOL_ID; } // Traverse the current whitelisted poolIDs and update the indicies of each pool that would contribute to arbitrage for it. // Maps poolID(arbToken2, arbToken3) => the indicies (within the whitelistedPools array) of the pools involved in WETH->arbToken2->arbToken3->WETH arbitrage. function updateArbitrageIndicies() public { bytes32[] memory poolIDs = poolsConfig.whitelistedPools(); for( uint256 i = 0; i < poolIDs.length; i++ ) { bytes32 poolID = poolIDs[i]; (IERC20 arbToken2, IERC20 arbToken3) = poolsConfig.underlyingTokenPair(poolID); // The middle two tokens can never be WETH in a valid arbitrage path as the path is WETH->arbToken2->arbToken3->WETH. if ( (arbToken2 != _weth) && (arbToken3 != _weth) ) { uint64 poolIndex1 = _poolIndex( _weth, arbToken2, poolIDs ); uint64 poolIndex2 = _poolIndex( arbToken2, arbToken3, poolIDs ); uint64 poolIndex3 = _poolIndex( arbToken3, _weth, poolIDs ); // Check if the indicies in storage have the correct values - and if not then update them ArbitrageIndicies memory indicies = _arbitrageIndicies[poolID]; if ( ( poolIndex1 != indicies.index1 ) || ( poolIndex2 != indicies.index2 ) || ( poolIndex3 != indicies.index3 ) ) _arbitrageIndicies[poolID] = ArbitrageIndicies(poolIndex1, poolIndex2, poolIndex3); } } } // Examine the arbitrage that has been generated since the last Upkeep.performUpkeep call and credit the pools that have contributed towards it. // The calculated sums for each pool will then be used to proportionally distribute ZERO token rewards to each of the contributing pools. function _calculateArbitrageProfits( bytes32[] memory poolIDs, uint256[] memory _calculatedProfits ) internal view { for( uint256 i = 0; i < poolIDs.length; i++ ) { // references poolID(arbToken2, arbToken3) which defines the arbitage path of WETH->arbToken2->arbToken3->WETH bytes32 poolID = poolIDs[i]; // Split the arbitrage profit between all the pools that contributed to generating the arbitrage for the referenced pool. uint256 arbitrageProfit = _arbitrageProfits[poolID] / 3; if ( arbitrageProfit > 0 ) { ArbitrageIndicies memory indicies = _arbitrageIndicies[poolID]; if ( indicies.index1 != INVALID_POOL_ID ) _calculatedProfits[indicies.index1] += arbitrageProfit; if ( indicies.index2 != INVALID_POOL_ID ) _calculatedProfits[indicies.index2] += arbitrageProfit; if ( indicies.index3 != INVALID_POOL_ID ) _calculatedProfits[indicies.index3] += arbitrageProfit; } } } // === VIEWS === // Look at the arbitrage that has been generated since the last performUpkeep and determine how much each of the pools contributed to those generated profits. // Returns the profits for all of the current whitelisted pools function profitsForWhitelistedPools() external view returns (uint256[] memory _calculatedProfits) { bytes32[] memory poolIDs = poolsConfig.whitelistedPools(); _calculatedProfits = new uint256[](poolIDs.length); _calculateArbitrageProfits( poolIDs, _calculatedProfits ); } function arbitrageIndicies(bytes32 poolID) external view returns (ArbitrageIndicies memory) { return _arbitrageIndicies[poolID]; } }
pragma solidity =0.8.22; import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; library PoolUtils { // Token reserves less than dust are treated as if they don't exist at all. // With the 18 decimals that are used for most tokens, DUST has a value of 0.0000000000000001 uint256 constant public DUST = 100; // A special pool that represents staked ZERO tokens that are not associated with any actual liquidity pool. bytes32 constant public STAKED_ZERO = bytes32(0); // Return the unique poolID for the given two tokens. // Tokens are sorted before being hashed to make reversed pairs equivalent. function _poolID( IERC20 tokenA, IERC20 tokenB ) internal pure returns (bytes32 poolID) { // See if the token orders are flipped if ( uint160(address(tokenB)) < uint160(address(tokenA)) ) return keccak256(abi.encodePacked(address(tokenB), address(tokenA))); return keccak256(abi.encodePacked(address(tokenA), address(tokenB))); } // Return the unique poolID and whether or not it is flipped function _poolIDAndFlipped( IERC20 tokenA, IERC20 tokenB ) internal pure returns (bytes32 poolID, bool flipped) { // See if the token orders are flipped if ( uint160(address(tokenB)) < uint160(address(tokenA)) ) return (keccak256(abi.encodePacked(address(tokenB), address(tokenA))), true); return (keccak256(abi.encodePacked(address(tokenA), address(tokenB))), false); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.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 // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (finance/VestingWallet.sol) pragma solidity ^0.8.0; import "../token/ERC20/utils/SafeERC20.sol"; import "../utils/Address.sol"; import "../utils/Context.sol"; /** * @title VestingWallet * @dev This contract handles the vesting of Eth and ERC20 tokens for a given beneficiary. Custody of multiple tokens * can be given to this contract, which will release the token to the beneficiary following a given vesting schedule. * The vesting schedule is customizable through the {vestedAmount} function. * * Any token transferred to this contract will follow the vesting schedule as if they were locked from the beginning. * Consequently, if the vesting has already started, any amount of tokens sent to this contract will (at least partly) * be immediately releasable. */ contract VestingWallet is Context { event EtherReleased(uint256 amount); event ERC20Released(address indexed token, uint256 amount); uint256 private _released; mapping(address => uint256) private _erc20Released; address private immutable _beneficiary; uint64 private immutable _start; uint64 private immutable _duration; /** * @dev Set the beneficiary, start timestamp and vesting duration of the vesting wallet. */ constructor(address beneficiaryAddress, uint64 startTimestamp, uint64 durationSeconds) payable { require(beneficiaryAddress != address(0), "VestingWallet: beneficiary is zero address"); _beneficiary = beneficiaryAddress; _start = startTimestamp; _duration = durationSeconds; } /** * @dev The contract should be able to receive Eth. */ receive() external payable virtual {} /** * @dev Getter for the beneficiary address. */ function beneficiary() public view virtual returns (address) { return _beneficiary; } /** * @dev Getter for the start timestamp. */ function start() public view virtual returns (uint256) { return _start; } /** * @dev Getter for the vesting duration. */ function duration() public view virtual returns (uint256) { return _duration; } /** * @dev Amount of eth already released */ function released() public view virtual returns (uint256) { return _released; } /** * @dev Amount of token already released */ function released(address token) public view virtual returns (uint256) { return _erc20Released[token]; } /** * @dev Getter for the amount of releasable eth. */ function releasable() public view virtual returns (uint256) { return vestedAmount(uint64(block.timestamp)) - released(); } /** * @dev Getter for the amount of releasable `token` tokens. `token` should be the address of an * IERC20 contract. */ function releasable(address token) public view virtual returns (uint256) { return vestedAmount(token, uint64(block.timestamp)) - released(token); } /** * @dev Release the native token (ether) that have already vested. * * Emits a {EtherReleased} event. */ function release() public virtual { uint256 amount = releasable(); _released += amount; emit EtherReleased(amount); Address.sendValue(payable(beneficiary()), amount); } /** * @dev Release the tokens that have already vested. * * Emits a {ERC20Released} event. */ function release(address token) public virtual { uint256 amount = releasable(token); _erc20Released[token] += amount; emit ERC20Released(token, amount); SafeERC20.safeTransfer(IERC20(token), beneficiary(), amount); } /** * @dev Calculates the amount of ether that has already vested. Default implementation is a linear vesting curve. */ function vestedAmount(uint64 timestamp) public view virtual returns (uint256) { return _vestingSchedule(address(this).balance + released(), timestamp); } /** * @dev Calculates the amount of tokens that has already vested. Default implementation is a linear vesting curve. */ function vestedAmount(address token, uint64 timestamp) public view virtual returns (uint256) { return _vestingSchedule(IERC20(token).balanceOf(address(this)) + released(token), timestamp); } /** * @dev Virtual implementation of the vesting formula. This returns the amount vested, as a function of time, for * an asset given its total historical allocation. */ function _vestingSchedule(uint256 totalAllocation, uint64 timestamp) internal view virtual returns (uint256) { if (timestamp < start()) { return 0; } else if (timestamp > start() + duration()) { return totalAllocation; } else { return (totalAllocation * (timestamp - start())) / duration(); } } }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import "./IStakingRewards.sol"; interface ILiquidity is IStakingRewards { function depositLiquidityAndIncreaseShare( IERC20 tokenA, IERC20 tokenB, uint256 maxAmountA, uint256 maxAmountB, uint256 minAddedAmountA, uint256 minAddedAmountB, uint256 minAddedLiquidity, uint256 deadline, bool useZapping ) external returns (uint256 addedLiquidity); function withdrawLiquidityAndClaim( IERC20 tokenA, IERC20 tokenB, uint256 liquidityToWithdraw, uint256 minReclaimedA, uint256 minReclaimedB, uint256 deadline ) external returns (uint256 reclaimedA, uint256 reclaimedB); }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; import "./IBootstrapBallot.sol"; import "./IAirdrop.sol"; interface IInitialDistribution { function distributionApproved( IAirdrop airdrop1, IAirdrop airdrop2 ) external; // Views function bootstrapBallot() external view returns (IBootstrapBallot); }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; import "../../staking/interfaces/IStakingRewards.sol"; interface IRewardsEmitter { function addZERORewards( AddedReward[] calldata addedRewards ) external; function performUpkeep( uint256 timeSinceLastUpkeep ) external; // Views function pendingRewardsForPools( bytes32[] calldata pools ) external view returns (uint256[] calldata); }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; import "./IRewardsEmitter.sol"; interface IZeroRewards { function sendInitialZERORewards( uint256 liquidityBootstrapAmount, bytes32[] calldata poolIDs ) external; function performUpkeep( bytes32[] calldata poolIDs, uint256[] calldata profitsForPools ) external; // Views function stakingRewardsEmitter() external view returns (IRewardsEmitter); function liquidityRewardsEmitter() external view returns (IRewardsEmitter); }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; interface IEmissions { function performUpkeep( uint256 timeSinceLastUpkeep ) external; }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; interface IAccessManager { function excludedCountriesUpdated() external; function grantAccess(bytes calldata signature) external; // Views function geoVersion() external view returns (uint256); function walletHasAccess(address wallet) external view returns (bool); }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; interface IAirdrop { function authorizeWallet( address wallet, uint256 zeroAmount ) external; function allowClaiming() external; function claim() external; // Views function claimedByUser( address wallet) external view returns (uint256); function claimingAllowed() external view returns (bool); function claimingStartTimestamp() external view returns (uint256); function claimableAmount(address wallet) external view returns (uint256); function airdropForUser( address wallet ) external view returns (uint256); }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; import "../../rewards/interfaces/IZeroRewards.sol"; import "../../pools/interfaces/IPools.sol"; import "../../interfaces/IZero.sol"; interface IDAO { function finalizeBallot( uint256 ballotID ) external; function manuallyRemoveBallot( uint256 ballotID ) external; function withdrawFromDAO( IERC20 token ) external returns (uint256 withdrawnAmount); // Views function pools() external view returns (IPools); function websiteURL() external view returns (string memory); function countryIsExcluded( string calldata country ) external view returns (bool); }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; interface IZero is IERC20 { function burnTokensInContract() external; // Views function totalBurned() external view returns (uint256); }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; interface IUpkeep { function performUpkeep() external; // Views function currentRewardsForCallingPerformUpkeep() external view returns (uint256); function lastUpkeepTimeEmissions() external view returns (uint256); function lastUpkeepTimeRewardsEmitters() external view returns (uint256); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; interface IPoolStats { // These are the indicies (in terms of a poolIDs location in the current whitelistedPoolIDs array) of pools involved in an arbitrage path struct ArbitrageIndicies { uint64 index1; uint64 index2; uint64 index3; } function clearProfitsForPools() external; function updateArbitrageIndicies() external; // Views function profitsForWhitelistedPools() external view returns (uint256[] memory _calculatedProfits); function arbitrageIndicies(bytes32 poolID) external view returns (ArbitrageIndicies memory); }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; struct AddedReward { bytes32 poolID; // The pool to add rewards to uint256 amountToAdd; // The amount of rewards (as ZERO) to add } struct UserShareInfo { uint256 userShare; // A user's share for a given poolID uint256 virtualRewards; // The amount of rewards that were added to maintain proper rewards/share ratio - and will be deducted from a user's pending rewards. uint256 cooldownExpiration; // The timestamp when the user can modify their share } interface IStakingRewards { function claimAllRewards( bytes32[] calldata poolIDs ) external returns (uint256 rewardsAmount); function addZERORewards( AddedReward[] calldata addedRewards ) external; // Views function totalShares(bytes32 poolID) external view returns (uint256); function totalSharesForPools( bytes32[] calldata poolIDs ) external view returns (uint256[] calldata shares); function totalRewardsForPools( bytes32[] calldata poolIDs ) external view returns (uint256[] calldata rewards); function userRewardForPool( address wallet, bytes32 poolID ) external view returns (uint256); function userShareForPool( address wallet, bytes32 poolID ) external view returns (uint256); function userVirtualRewardsForPool( address wallet, bytes32 poolID ) external view returns (uint256); function userRewardsForPools( address wallet, bytes32[] calldata poolIDs ) external view returns (uint256[] calldata rewards); function userShareForPools( address wallet, bytes32[] calldata poolIDs ) external view returns (uint256[] calldata shares); function userCooldowns( address wallet, bytes32[] calldata poolIDs ) external view returns (uint256[] calldata cooldowns); }
// SPDX-License-Identifier: BUSL 1.1 pragma solidity =0.8.22; interface IBootstrapBallot { function vote( bool voteStartExchangeYes, uint256 zeroAmount, bytes calldata signature ) external; function finalizeBallot() external; function authorizeAirdrop2( uint256 zeroAmount, bytes calldata signature ) external; function finalizeAirdrop2() external; // Views function claimableTimestamp1() external view returns (uint256); function claimableTimestamp2() external view returns (uint256); function hasVoted(address user) external view returns (bool); function ballotFinalized() external view returns (bool); function startExchangeYes() external view returns (uint256); function startExchangeNo() external view returns (uint256); }
{ "remappings": [ "chainlink/=lib/chainlink/", "ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "forge-std/=lib/openzeppelin-contracts/lib/forge-std/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "openzeppelin/=lib/openzeppelin-contracts/contracts/", "v3-core/=lib/v3-core/contracts/" ], "optimizer": { "enabled": true, "runs": 10000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract IExchangeConfig","name":"_exchangeConfig","type":"address"},{"internalType":"contract IPoolsConfig","name":"_poolsConfig","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"tokenA","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"tokenB","type":"address"},{"indexed":false,"internalType":"uint256","name":"addedAmountA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"addedAmountB","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"addedLiquidity","type":"uint256"}],"name":"LiquidityAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"tokenA","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"tokenB","type":"address"},{"indexed":false,"internalType":"uint256","name":"reclaimedA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reclaimedB","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"removedLiquidity","type":"uint256"}],"name":"LiquidityRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"swapTokenIn","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"swapTokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"swapAmountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"swapAmountOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"arbitrageProfit","type":"uint256"}],"name":"SwapAndArbitrage","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenWithdrawal","type":"event"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"_arbitrageIndicies","outputs":[{"internalType":"uint64","name":"index1","type":"uint64"},{"internalType":"uint64","name":"index2","type":"uint64"},{"internalType":"uint64","name":"index3","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"_arbitrageProfits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_weth","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"tokenA","type":"address"},{"internalType":"contract IERC20","name":"tokenB","type":"address"},{"internalType":"uint256","name":"maxAmountA","type":"uint256"},{"internalType":"uint256","name":"maxAmountB","type":"uint256"},{"internalType":"uint256","name":"minAddedAmountA","type":"uint256"},{"internalType":"uint256","name":"minAddedAmountB","type":"uint256"},{"internalType":"uint256","name":"totalLiquidity","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"addedAmountA","type":"uint256"},{"internalType":"uint256","name":"addedAmountB","type":"uint256"},{"internalType":"uint256","name":"addedLiquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"poolID","type":"bytes32"}],"name":"arbitrageIndicies","outputs":[{"components":[{"internalType":"uint64","name":"index1","type":"uint64"},{"internalType":"uint64","name":"index2","type":"uint64"},{"internalType":"uint64","name":"index3","type":"uint64"}],"internalType":"struct IPoolStats.ArbitrageIndicies","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"clearProfitsForPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dao","outputs":[{"internalType":"contract IDAO","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"swapTokenIn","type":"address"},{"internalType":"contract IERC20","name":"swapTokenMiddle","type":"address"},{"internalType":"contract IERC20","name":"swapTokenOut","type":"address"},{"internalType":"uint256","name":"swapAmountIn","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"depositDoubleSwapWithdraw","outputs":[{"internalType":"uint256","name":"swapAmountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"swapTokenIn","type":"address"},{"internalType":"contract IERC20","name":"swapTokenOut","type":"address"},{"internalType":"uint256","name":"swapAmountIn","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"depositSwapWithdraw","outputs":[{"internalType":"uint256","name":"swapAmountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"zapSwapTokenIn","type":"address"},{"internalType":"contract IERC20","name":"zapSwapTokenOut","type":"address"},{"internalType":"uint256","name":"zapSwapAmountIn","type":"uint256"}],"name":"depositZapSwapWithdraw","outputs":[{"internalType":"uint256","name":"zapSwapAmountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"depositedUserBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exchangeConfig","outputs":[{"internalType":"contract IExchangeConfig","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exchangeIsLive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"tokenA","type":"address"},{"internalType":"contract IERC20","name":"tokenB","type":"address"}],"name":"getPoolReserves","outputs":[{"internalType":"uint256","name":"reserveA","type":"uint256"},{"internalType":"uint256","name":"reserveB","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidity","outputs":[{"internalType":"contract ILiquidity","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolsConfig","outputs":[{"internalType":"contract IPoolsConfig","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"profitsForWhitelistedPools","outputs":[{"internalType":"uint256[]","name":"_calculatedProfits","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"tokenA","type":"address"},{"internalType":"contract IERC20","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidityToRemove","type":"uint256"},{"internalType":"uint256","name":"minReclaimedA","type":"uint256"},{"internalType":"uint256","name":"minReclaimedB","type":"uint256"},{"internalType":"uint256","name":"totalLiquidity","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"reclaimedA","type":"uint256"},{"internalType":"uint256","name":"reclaimedB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IDAO","name":"_dao","type":"address"},{"internalType":"contract ILiquidity","name":"_liquidity","type":"address"}],"name":"setContracts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startExchangeApproved","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"swapTokenIn","type":"address"},{"internalType":"contract IERC20","name":"swapTokenOut","type":"address"},{"internalType":"uint256","name":"swapAmountIn","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swap","outputs":[{"internalType":"uint256","name":"swapAmountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateArbitrageIndicies","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"usdc","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"zero","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
6101406040523480156200001257600080fd5b50604051620044c4380380620044c4833981016040819052620000359162000334565b60016000556001600160a01b03808316608081905290821660a05260408051633fc8cef360e01b81529051849283928592633fc8cef3916004808201926020929091908290030181865afa15801562000092573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000b8919062000373565b6001600160a01b031660c0816001600160a01b0316815250505050806001600160a01b0316633fc8cef36040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000112573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000138919062000373565b6001600160a01b031660e0816001600160a01b031681525050806001600160a01b031663bc1b392d6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000190573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001b6919062000373565b6001600160a01b0316610100816001600160a01b031681525050806001600160a01b0316633e413bee6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200020f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000235919062000373565b6001600160a01b031661012052506200024e33620002c9565b816001600160a01b031663bc1b392d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200028d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002b3919062000373565b6001600160a01b031661010052506200039a9050565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b03811681146200033157600080fd5b50565b600080604083850312156200034857600080fd5b825162000355816200031b565b602084015190925062000368816200031b565b809150509250929050565b6000602082840312156200038657600080fd5b815162000393816200031b565b9392505050565b60805160a05160c05160e051610100516101205161401d620004a76000396000818161028401528181612e4f0152612ee801526000818161055501528181612db901528181612e2c01528181612eae01528181612f0b01528181612f6b01528181612fd10152818161344101526134fa0152600081816102ab01528181612df201528181612e7501528181612f3101528181612f940152818161300f015281816130d8015261342001526000818161050e01528181611677015281816116b4015281816116f5015261173401526000818161039f015281816105ff01528181611530015281816115e50152611a060152600081816101e4015281816118d60152611b95015261401d6000f3fe608060405234801561001057600080fd5b50600436106101da5760003560e01c80637a950f9911610104578063c4de1782116100a2578063d8952a4911610071578063d8952a49146105ad578063e2f7c480146105c0578063f2fde38b146105d3578063f3fef3a3146105e657600080fd5b8063c4de178214610577578063c74e31c81461057f578063cb936cc114610592578063d4469c3e1461059a57600080fd5b80638da5cb5b116100de5780638da5cb5b146104f8578063a176459514610509578063af42bb1a14610530578063bc1b392d1461055057600080fd5b80637a950f99146104af5780637ae06e58146104c257806385876e95146104f057600080fd5b80634162169f1161017c57806352c19b5a1161014b57806352c19b5a1461039a578063634040b3146103c1578063647e77d0146103f6578063715018a6146104a757600080fd5b80634162169f146102cd57806344483d53146102e057806347e7ef2414610308578063516a5c251461031d57600080fd5b80632458a800116101b85780632458a8001461024b5780632c6628571461026c5780633e413bee1461027f5780633fc8cef3146102a657600080fd5b80630e2789bb146101df5780631959df06146102235780631a68650214610238575b600080fd5b6102067f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b61022b6105f9565b60405161021a9190613a0d565b600554610206906001600160a01b031681565b61025e610259366004613a66565b6106d9565b60405190815260200161021a565b61025e61027a366004613aa7565b610952565b6102067f000000000000000000000000000000000000000000000000000000000000000081565b6102067f000000000000000000000000000000000000000000000000000000000000000081565b600454610206906001600160a01b031681565b6102f36102ee366004613ae0565b61097f565b6040805192835260208301919091520161021a565b61031b610316366004613b39565b610e74565b005b61036f61032b366004613b65565b60026020526000908152604090205467ffffffffffffffff808216916801000000000000000081048216917001000000000000000000000000000000009091041683565b6040805167ffffffffffffffff9485168152928416602084015292169181019190915260600161021a565b6102067f000000000000000000000000000000000000000000000000000000000000000081565b6005546103e69074010000000000000000000000000000000000000000900460ff1681565b604051901515815260200161021a565b610474610404366004613b65565b6040805160608082018352600080835260208084018290529284018190529384526002825292829020825193840183525467ffffffffffffffff80821685526801000000000000000082048116928501929092527001000000000000000000000000000000009004169082015290565b60408051825167ffffffffffffffff9081168252602080850151821690830152928201519092169082015260600161021a565b61031b610f69565b61025e6104bd366004613b7e565b610f7d565b6104d56104d0366004613bcf565b6110fe565b6040805193845260208401929092529082015260600161021a565b61031b61152c565b6003546001600160a01b0316610206565b6102067f000000000000000000000000000000000000000000000000000000000000000081565b61025e61053e366004613b65565b60016020526000908152604090205481565b6102067f000000000000000000000000000000000000000000000000000000000000000081565b61031b6118d4565b61025e61058d366004613c31565b611ad2565b61031b611b8b565b6102f36105a8366004613aa7565b611d73565b61031b6105bb366004613aa7565b611dee565b61025e6105ce366004613b7e565b611e46565b61031b6105e1366004613c96565b611ee3565b61031b6105f4366004613b39565b611f73565b606060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663434798856040518163ffffffff1660e01b8152600401600060405180830381865afa15801561065b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106839190810190613ce2565b9050805167ffffffffffffffff81111561069f5761069f613cb3565b6040519080825280602002602001820160405280156106c8578160200160208202803683370190505b5091506106d581836120ee565b5090565b6005546000906001600160a01b031633146107875760405162461bcd60e51b815260206004820152604960248201527f506f6f6c732e6465706f7369745a61705377617057697468647261772069732060448201527f6f6e6c792063616c6c61626c652066726f6d20746865204c697175696469747960648201527f20636f6e74726163740000000000000000000000000000000000000000000000608482015260a4015b60405180910390fd5b61079c6001600160a01b03851633308561228e565b6000806107a98686612345565b6000828152600660205260409020919350915081156108815780546107f69060649070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16613dfe565b6fffffffffffffffffffffffffffffffff16851061087c5760405162461bcd60e51b815260206004820152602760248201527f43616e6e6f74207a6170206d6f7265207468616e203125206f6620746865207260448201527f6573657276657300000000000000000000000000000000000000000000000000606482015260840161077e565b610927565b80546108a1906064906fffffffffffffffffffffffffffffffff16613dfe565b6fffffffffffffffffffffffffffffffff1685106109275760405162461bcd60e51b815260206004820152602760248201527f43616e6e6f74207a6170206d6f7265207468616e203125206f6620746865207260448201527f6573657276657300000000000000000000000000000000000000000000000000606482015260840161077e565b61093281838761241f565b93506109486001600160a01b03871633866125c4565b5050509392505050565b6001600160a01b038083166000908152600760209081526040808320938516835292905220545b92915050565b60008061098a61260d565b6005546001600160a01b03163314610a305760405162461bcd60e51b815260206004820152604260248201527f506f6f6c732e72656d6f76654c6971756964697479206973206f6e6c7920636160448201527f6c6c61626c652066726f6d20746865204c697175696469747920636f6e74726160648201527f6374000000000000000000000000000000000000000000000000000000000000608482015260a40161077e565b60008611610aa65760405162461bcd60e51b815260206004820152602e60248201527f54686520616d6f756e74206f66206c6971756964697479546f52656d6f76652060448201527f63616e6e6f74206265207a65726f000000000000000000000000000000000000606482015260840161077e565b600080610ab38a8a612345565b60008281526006602052604090208054929450909250906fffffffffffffffffffffffffffffffff70010000000000000000000000000000000082048116911611610b6e5780548690610b19908b906fffffffffffffffffffffffffffffffff16613e2d565b610b239190613e44565b81549095506fffffffffffffffffffffffffffffffff80821691610b5d918891700100000000000000000000000000000000900416613e2d565b610b679190613e44565b9350610bf3565b80548690610ba3908b9070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16613e2d565b610bad9190613e44565b81549094506fffffffffffffffffffffffffffffffff7001000000000000000000000000000000008204811691610be691879116613e2d565b610bf09190613e44565b94505b805485908290600090610c199084906fffffffffffffffffffffffffffffffff16613e58565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550838160000160108282829054906101000a90046fffffffffffffffffffffffffffffffff16610c7e9190613e58565b82546101009290920a6fffffffffffffffffffffffffffffffff8181021990931691831602179091558254606491161080159150610ce55750805460647001000000000000000000000000000000009091046fffffffffffffffffffffffffffffffff1610155b610d575760405162461bcd60e51b815260206004820152602d60248201527f496e73756666696369656e74207265736572766573206166746572206c69717560448201527f69646974792072656d6f76616c00000000000000000000000000000000000000606482015260840161077e565b8115610d61579293925b878510158015610d715750868410155b610de35760405162461bcd60e51b815260206004820152602760248201527f496e73756666696369656e7420756e6465726c79696e6720746f6b656e73207260448201527f657475726e656400000000000000000000000000000000000000000000000000606482015260840161077e565b610df76001600160a01b038c1633876125c4565b610e0b6001600160a01b038b1633866125c4565b60408051868152602081018690529081018a90526001600160a01b03808c1691908d16907fbc7d19d505c7ec4db83f3b51f19fb98c4c8a99922e7839d1ee608dfbee29501b9060600160405180910390a3505050610e696001600055565b965096945050505050565b610e7c61260d565b60648111610ecc5760405162461bcd60e51b815260206004820152601860248201527f4465706f73697420616d6f756e7420746f6f20736d616c6c0000000000000000604482015260640161077e565b3360009081526007602090815260408083206001600160a01b038616845290915281208054839290610eff908490613e88565b90915550610f1a90506001600160a01b03831633308461228e565b6040518181526001600160a01b0383169033907f98c09d9949722bae4bd0d988d4050091c3ae7ec6d51d3c6bbfe4233593944e9e906020015b60405180910390a3610f656001600055565b5050565b610f71612666565b610f7b60006126c0565b565b6000610f8761260d565b8180421115610fd85760405162461bcd60e51b815260206004820152600a60248201527f5458204558504952454400000000000000000000000000000000000000000000604482015260640161077e565b3360009081526007602090815260408083206001600160a01b038b16845291829052909120548611156110735760405162461bcd60e51b815260206004820152603560248201527f496e73756666696369656e74206465706f736974656420746f6b656e2062616c60448201527f616e6365206f6620696e697469616c20746f6b656e0000000000000000000000606482015260840161077e565b6001600160a01b0388166000908152602082905260408120805488929061109b908490613e9b565b909155506110ad90508888888861272a565b925082816000896001600160a01b03166001600160a01b0316815260200190815260200160002060008282546110e39190613e88565b90915550506001600055506110f59050565b95945050505050565b600080600061110b61260d565b6005546001600160a01b0316331461118b5760405162461bcd60e51b815260206004820152603f60248201527f506f6f6c732e6164644c6971756964697479206973206f6e6c792063616c6c6160448201527f626c652066726f6d20746865204c697175696469747920636f6e747261637400606482015260840161077e565b60055474010000000000000000000000000000000000000000900460ff166111f55760405162461bcd60e51b815260206004820152601c60248201527f5468652065786368616e6765206973206e6f7420796574206c69766500000000604482015260640161077e565b886001600160a01b03168a6001600160a01b03160361127c5760405162461bcd60e51b815260206004820152602960248201527f43616e6e6f7420616464206c697175696469747920666f72206475706c69636160448201527f746520746f6b656e730000000000000000000000000000000000000000000000606482015260840161077e565b606488116112f25760405162461bcd60e51b815260206004820152602860248201527f54686520616d6f756e74206f6620746f6b656e4120746f20616464206973207460448201527f6f6f20736d616c6c000000000000000000000000000000000000000000000000606482015260840161077e565b606487116113685760405162461bcd60e51b815260206004820152602860248201527f54686520616d6f756e74206f6620746f6b656e4220746f20616464206973207460448201527f6f6f20736d616c6c000000000000000000000000000000000000000000000000606482015260840161077e565b6000806113758c8c612345565b9150915080156113985761138b828a8c89612911565b90965090945092506113ac565b6113a4828b8b89612911565b919650945092505b878510156114225760405162461bcd60e51b815260206004820152602660248201527f496e73756666696369656e7420746f6b656e4120616464656420746f206c697160448201527f7569646974790000000000000000000000000000000000000000000000000000606482015260840161077e565b868410156114985760405162461bcd60e51b815260206004820152602660248201527f496e73756666696369656e7420746f6b656e4220616464656420746f206c697160448201527f7569646974790000000000000000000000000000000000000000000000000000606482015260840161077e565b6114ad6001600160a01b038d1633308861228e565b6114c26001600160a01b038c1633308761228e565b60408051868152602081018690529081018490526001600160a01b03808d1691908e16907f4a1a2a6176e9646d9e3157f7c2ab3c499f18337c0b0828cfb28e0a61de4a11f79060600160405180910390a3505061151f6001600055565b9750975097945050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663434798856040518163ffffffff1660e01b8152600401600060405180830381865afa15801561158c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115b49190810190613ce2565b905060005b8151811015610f655760008282815181106115d6576115d6613eae565b602002602001015190506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c2fb9aac846040518263ffffffff1660e01b815260040161163191815260200190565b6040805180830381865afa15801561164d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116719190613edd565b915091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316141580156116e957507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b031614155b156118c957600061171b7f00000000000000000000000000000000000000000000000000000000000000008488612c6b565b9050600061172a848489612c6b565b90506000611759847f00000000000000000000000000000000000000000000000000000000000000008a612c6b565b6000878152600260209081526040918290208251606081018452905467ffffffffffffffff8082168084526801000000000000000083048216948401949094527001000000000000000000000000000000009091048116938201939093529293509085161415806117e25750806020015167ffffffffffffffff168367ffffffffffffffff1614155b806118055750806040015167ffffffffffffffff168267ffffffffffffffff1614155b156118c4576040805160608101825267ffffffffffffffff8087168252858116602080840191825286831684860190815260008d8152600290925294902092518354915194518316700100000000000000000000000000000000027fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff95841668010000000000000000027fffffffffffffffffffffffffffffffff00000000000000000000000000000000909316919093161717929092169190911790555b505050505b5050506001016115b9565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632cf4704a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611932573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119569190613f0c565b6001600160a01b0316336001600160a01b031614611a025760405162461bcd60e51b815260206004820152604860248201527f506f6f6c53746174732e636c65617250726f66697473466f72506f6f6c73206960448201527f73206f6e6c792063616c6c61626c652066726f6d207468652055706b6565702060648201527f636f6e7472616374000000000000000000000000000000000000000000000000608482015260a40161077e565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663434798856040518163ffffffff1660e01b8152600401600060405180830381865afa158015611a62573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a8a9190810190613ce2565b905060005b8151811015610f65576001806000848481518110611aaf57611aaf613eae565b602090810291909101810151825281019190915260400160002055600101611a8f565b6000611adc61260d565b8180421115611b2d5760405162461bcd60e51b815260206004820152600a60248201527f5458204558504952454400000000000000000000000000000000000000000000604482015260640161077e565b611b426001600160a01b03891633308861228e565b6000611b51898988600061272a565b9050611b5f8888838861272a565b9250611b756001600160a01b03881633856125c4565b5050611b816001600055565b9695505050505050565b611b9361260d565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166395645e346040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bf1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c159190613f0c565b6001600160a01b031663bd1d4b286040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c769190613f0c565b6001600160a01b0316336001600160a01b031614611d225760405162461bcd60e51b815260206004820152605060248201527f506f6f6c732e737461727445786368616e6765417070726f7665642063616e2060448201527f6f6e6c792062652063616c6c65642066726f6d2074686520426f6f747374726160648201527f7042616c6c6f7420636f6e747261637400000000000000000000000000000000608482015260a40161077e565b611d2a61152c565b600580547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000179055610f7b6001600055565b600080600080611d838686612345565b6000828152600660209081526040918290208251808401909352546fffffffffffffffffffffffffffffffff808216808552700100000000000000000000000000000000909204169183018290529750955091935091508115611de4579293925b5050509250929050565b611df6612666565b600480546001600160a01b038085167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179092556005805492841692909116919091179055610f65610f69565b6000611e5061260d565b8180421115611ea15760405162461bcd60e51b815260206004820152600a60248201527f5458204558504952454400000000000000000000000000000000000000000000604482015260640161077e565b611eb66001600160a01b03881633308861228e565b611ec28787878761272a565b9150611ed86001600160a01b03871633846125c4565b506110f56001600055565b611eeb612666565b6001600160a01b038116611f675760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161077e565b611f70816126c0565b50565b611f7b61260d565b3360009081526007602090815260408083206001600160a01b03861684529091529020548111156120145760405162461bcd60e51b815260206004820152603160248201527f496e73756666696369656e742062616c616e636520746f20776974686472617760448201527f2073706563696669656420616d6f756e74000000000000000000000000000000606482015260840161077e565b606481116120645760405162461bcd60e51b815260206004820152601960248201527f576974686472617720616d6f756e7420746f6f20736d616c6c00000000000000604482015260640161077e565b3360009081526007602090815260408083206001600160a01b038616845290915281208054839290612097908490613e9b565b909155506120b190506001600160a01b03831633836125c4565b6040518181526001600160a01b0383169033907f42856d0378dde02337bb59ae41747abc77ded8ebdbbc5cbdd1e53693b755493890602001610f53565b60005b825181101561228957600083828151811061210e5761210e613eae565b6020026020010151905060006003600160008481526020019081526020016000205461213a9190613e44565b9050801561227f576000828152600260209081526040918290208251606081018452905467ffffffffffffffff80821680845268010000000000000000830482169484019490945270010000000000000000000000000000000090910481169382019390935291146121df578185826000015167ffffffffffffffff16815181106121c7576121c7613eae565b602002602001018181516121db9190613e88565b9052505b602081015167ffffffffffffffff9081161461222e578185826020015167ffffffffffffffff168151811061221657612216613eae565b6020026020010181815161222a9190613e88565b9052505b604081015167ffffffffffffffff9081161461227d578185826040015167ffffffffffffffff168151811061226557612265613eae565b602002602001018181516122799190613e88565b9052505b505b50506001016120f1565b505050565b6040516001600160a01b038085166024830152831660448201526064810182905261233f9085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152612ccc565b50505050565b600080836001600160a01b0316836001600160a01b031610156123bf576040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606085811b8216602084015286901b16603482015260480160405160208183030381529060405280519060200120600191509150612418565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086811b8216602084015285901b166034820152604801604051602081830303815290604052805190602001206000915091505b9250929050565b82546000906fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041684156124885761245e8482613e88565b90508061246b8584613e2d565b6124759190613e44565b92506124818383613e9b565b91506124b8565b6124928483613e88565b91508161249f8583613e2d565b6124a99190613e44565b92506124b58382613e9b565b90505b6064821180156124c85750606481115b6125145760405162461bcd60e51b815260206004820181905260248201527f496e73756666696369656e742072657365727665732061667465722073776170604482015260640161077e565b6fffffffffffffffffffffffffffffffff821180159061254457506fffffffffffffffffffffffffffffffff8111155b6125905760405162461bcd60e51b815260206004820152601c60248201527f5265736572766573206f766572666c6f77206166746572207377617000000000604482015260640161077e565b6fffffffffffffffffffffffffffffffff908116700100000000000000000000000000000000029116179093555090919050565b6040516001600160a01b0383166024820152604481018290526122899084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064016122db565b60026000540361265f5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161077e565b6002600055565b6003546001600160a01b03163314610f7b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161077e565b600380546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60008060006127398787612345565b600082815260066020526040902080549294509092509060646fffffffffffffffffffffffffffffffff90911611801561279b5750805460647001000000000000000000000000000000009091046fffffffffffffffffffffffffffffffff16115b61280d5760405162461bcd60e51b815260206004820152602160248201527f496e73756666696369656e74207265736572766573206265666f72652073776160448201527f7000000000000000000000000000000000000000000000000000000000000000606482015260840161077e565b61281881838861241f565b9350848410156128905760405162461bcd60e51b815260206004820152602360248201527f496e73756666696369656e7420726573756c74696e6720746f6b656e20616d6f60448201527f756e740000000000000000000000000000000000000000000000000000000000606482015260840161077e565b60008061289d8a8a612db4565b9150915060006128ad8383612ffc565b604080518b8152602081018a90529081018290529091506001600160a01b03808c1691908d169033907f1195a8db0f3ef828b684c3193db62af33dae2817d69fa75433df4c896c00be189060600160405180910390a4505050505050949350505050565b60008481526006602052604081208054829182916fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041681158061295a575080155b15612a3c578254899084906000906129859084906fffffffffffffffffffffffffffffffff16613f29565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550878360000160108282829054906101000a90046fffffffffffffffffffffffffffffffff166129ea9190613f29565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055508888898b612a2e9190613e88565b955095509550505050612c61565b600082612a49838c613e2d565b612a539190613e44565b905088811115612a7c5781612a68848b613e2d565b612a729190613e44565b9650889550612a83565b8996508095505b60648711612af95760405162461bcd60e51b815260206004820152602a60248201527f4164646564206c697175696469747920666f7220746f6b656e2030206c65737360448201527f207468616e204455535400000000000000000000000000000000000000000000606482015260840161077e565b60648611612b6f5760405162461bcd60e51b815260206004820152602a60248201527f4164646564206c697175696469747920666f7220746f6b656e2031206c65737360448201527f207468616e204455535400000000000000000000000000000000000000000000606482015260840161077e565b835487908590600090612b959084906fffffffffffffffffffffffffffffffff16613f29565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550858460000160108282829054906101000a90046fffffffffffffffffffffffffffffffff16612bfa9190613f29565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055508183612c3c9190613e88565b612c468789613e88565b612c50908a613e2d565b612c5a9190613e44565b9450505050505b9450945094915050565b600080612c7885856131d5565b905060005b8351811015612cb757838181518110612c9857612c98613eae565b60200260200101518203612caf579150612cc59050565b600101612c7d565b5067ffffffffffffffff9150505b9392505050565b6000612d21826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166132a49092919063ffffffff16565b9050805160001480612d42575080806020019051810190612d429190613f52565b6122895760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161077e565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b031603612e73577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031603612e7357507f000000000000000000000000000000000000000000000000000000000000000090507f0000000000000000000000000000000000000000000000000000000000000000612418565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b031603612f2f577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031603612f2f57507f000000000000000000000000000000000000000000000000000000000000000090507f0000000000000000000000000000000000000000000000000000000000000000612418565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b031603612f9257507f0000000000000000000000000000000000000000000000000000000000000000905081612418565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031603612ff557508290507f0000000000000000000000000000000000000000000000000000000000000000612418565b5090929050565b60008060008060008060008060006130347f00000000000000000000000000000000000000000000000000000000000000008c612345565b60008281526006602052604090208054929a5090985094506fffffffffffffffffffffffffffffffff80821691700100000000000000000000000000000000900416881561307e57905b6130888d8d612345565b60008281526006602052604090208054929c5090995095506fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041689156130d257905b6130fc8e7f0000000000000000000000000000000000000000000000000000000000000000612345565b60008281526006602052604090208054929e50909a5096506fffffffffffffffffffffffffffffffff808216917001000000000000000000000000000000009004168a1561314657905b6064861180156131565750606485115b80156131625750606484115b801561316e5750606483115b801561317a5750606482115b80156131865750606481115b1561319c576131998686868686866132bb565b96505b50505050505060008111156131c7576131ba818585858b8b8b6133e2565b98506131c78b8b8b61354f565b505050505050505092915050565b6000826001600160a01b0316826001600160a01b0316101561324a576040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084811b8216602084015285901b166034820152604801604051602081830303815290604052805190602001209050610979565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606085811b8216602084015284901b16603482015260480160405160208183030381529060405280519060200120905092915050565b60606132b3848460008561358b565b949350505050565b6000806132cc8887868a898861367d565b905080156132ed5796871c9695861c9594851c9493841c9392831c9291821c915b8786028402878602840281811161330a5760009350505050611b81565b868902888a01870201600061331e836136b8565b613327856136b8565b029050818482038161333b5761333b613da0565b0495508560000361335457600095505050505050611b81565b8415613377579a841b9a99841b9998841b9897841b9796841b9695841b9594841b945b6000868d01878d028161338c5761338c613da0565b049050808b01818b02816133a2576133a2613da0565b049050808901818902816133b8576133b8613da0565b049050868110156133d25760009650505050505050611b81565b5050505050509695505050505050565b6000806133f088868b61241f565b90506133fd87858361241f565b905061340a86848361241f565b90506134168982613e9b565b91506000806134657f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612345565b600082815260066020526040902080549294509092509060646fffffffffffffffffffffffffffffffff9091161180156134c75750805460647001000000000000000000000000000000009091046fffffffffffffffffffffffffffffffff16115b156135405760006134d982848861241f565b6004546001600160a01b0390811660009081526007602090815260408083207f0000000000000000000000000000000000000000000000000000000000000000909416835292905290812080549293508392909190613539908490613e88565b9091555050505b50505050979650505050505050565b600061355b84846131d5565b9050816001600083815260200190815260200160002060008282546135809190613e88565b909155505050505050565b6060824710156136035760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161077e565b600080866001600160a01b0316858760405161361f9190613f98565b60006040518083038185875af1925050503d806000811461365c576040519150601f19603f3d011682016040523d82523d6000602084013e613661565b606091505b5091509150613672878383876137a0565b979650505050505050565b60008061368b888888613819565b9050600061369a868686613819565b90508082116136a957806136ab565b815b9998505050505050505050565b6000816000036136ca57506000919050565b600060016136d78461385c565b901c6001901b905060018184816136f0576136f0613da0565b048201901c9050600181848161370857613708613da0565b048201901c9050600181848161372057613720613da0565b048201901c9050600181848161373857613738613da0565b048201901c9050600181848161375057613750613da0565b048201901c9050600181848161376857613768613da0565b048201901c9050600181848161378057613780613da0565b048201901c9050612cc58182858161379a5761379a613da0565b046138f0565b6060831561380f578251600003613808576001600160a01b0385163b6138085760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161077e565b50816132b3565b6132b38383613906565b60008061382583613930565b61382e85613930565b61383787613930565b0101905060f081101561384e576000915050612cc5565b6110f560f0820360036139d6565b600080608083901c1561387157608092831c92015b604083901c1561388357604092831c92015b602083901c1561389557602092831c92015b601083901c156138a757601092831c92015b600883901c156138b957600892831c92015b600483901c156138cb57600492831c92015b600283901c156138dd57600292831c92015b600183901c156109795760010192915050565b60008183106138ff5781612cc5565b5090919050565b8151156139165781518083602001fd5b8060405162461bcd60e51b815260040161077e9190613fb4565b6000700100000000000000000000000000000000821061395257608091821c91015b68010000000000000000821061396a57604091821c91015b640100000000821061397e57602091821c91015b62010000821061399057601091821c91015b61010082106139a157600891821c91015b601082106139b157600491821c91015b600482106139c157600291821c91015b600282106139d157600191821c91015b919050565b60008215613a0457816139ea600185613e9b565b6139f49190613e44565b6139ff906001613e88565b612cc5565b50600092915050565b6020808252825182820181905260009190848201906040850190845b81811015613a4557835183529284019291840191600101613a29565b50909695505050505050565b6001600160a01b0381168114611f7057600080fd5b600080600060608486031215613a7b57600080fd5b8335613a8681613a51565b92506020840135613a9681613a51565b929592945050506040919091013590565b60008060408385031215613aba57600080fd5b8235613ac581613a51565b91506020830135613ad581613a51565b809150509250929050565b60008060008060008060c08789031215613af957600080fd5b8635613b0481613a51565b95506020870135613b1481613a51565b95989597505050506040840135936060810135936080820135935060a0909101359150565b60008060408385031215613b4c57600080fd5b8235613b5781613a51565b946020939093013593505050565b600060208284031215613b7757600080fd5b5035919050565b600080600080600060a08688031215613b9657600080fd5b8535613ba181613a51565b94506020860135613bb181613a51565b94979496505050506040830135926060810135926080909101359150565b600080600080600080600060e0888a031215613bea57600080fd5b8735613bf581613a51565b96506020880135613c0581613a51565b96999698505050506040850135946060810135946080820135945060a0820135935060c0909101359150565b60008060008060008060c08789031215613c4a57600080fd5b8635613c5581613a51565b95506020870135613c6581613a51565b94506040870135613c7581613a51565b959894975094956060810135955060808101359460a0909101359350915050565b600060208284031215613ca857600080fd5b8135612cc581613a51565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020808385031215613cf557600080fd5b825167ffffffffffffffff80821115613d0d57600080fd5b818501915085601f830112613d2157600080fd5b815181811115613d3357613d33613cb3565b8060051b604051601f19603f83011681018181108582111715613d5857613d58613cb3565b604052918252848201925083810185019188831115613d7657600080fd5b938501935b82851015613d9457845184529385019392850192613d7b565b98975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006fffffffffffffffffffffffffffffffff80841680613e2157613e21613da0565b92169190910492915050565b808202811582820484141761097957610979613dcf565b600082613e5357613e53613da0565b500490565b6fffffffffffffffffffffffffffffffff828116828216039080821115613e8157613e81613dcf565b5092915050565b8082018082111561097957610979613dcf565b8181038181111561097957610979613dcf565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008060408385031215613ef057600080fd5b8251613efb81613a51565b6020840151909250613ad581613a51565b600060208284031215613f1e57600080fd5b8151612cc581613a51565b6fffffffffffffffffffffffffffffffff818116838216019080821115613e8157613e81613dcf565b600060208284031215613f6457600080fd5b81518015158114612cc557600080fd5b60005b83811015613f8f578181015183820152602001613f77565b50506000910152565b60008251613faa818460208701613f74565b9190910192915050565b6020815260008251806020840152613fd3816040850160208701613f74565b601f01601f1916919091016040019291505056fea2646970667358221220f3d4061a98a4f87ea126f9cf151a278e3aec480c7d5dff3a7e6fc65d66181d4d64736f6c63430008160033000000000000000000000000f2fed4a7a8d1cc2db73ae0439e07db21d0d58bd90000000000000000000000000ac992636f863504039a028a6e9a2c192529c487
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101da5760003560e01c80637a950f9911610104578063c4de1782116100a2578063d8952a4911610071578063d8952a49146105ad578063e2f7c480146105c0578063f2fde38b146105d3578063f3fef3a3146105e657600080fd5b8063c4de178214610577578063c74e31c81461057f578063cb936cc114610592578063d4469c3e1461059a57600080fd5b80638da5cb5b116100de5780638da5cb5b146104f8578063a176459514610509578063af42bb1a14610530578063bc1b392d1461055057600080fd5b80637a950f99146104af5780637ae06e58146104c257806385876e95146104f057600080fd5b80634162169f1161017c57806352c19b5a1161014b57806352c19b5a1461039a578063634040b3146103c1578063647e77d0146103f6578063715018a6146104a757600080fd5b80634162169f146102cd57806344483d53146102e057806347e7ef2414610308578063516a5c251461031d57600080fd5b80632458a800116101b85780632458a8001461024b5780632c6628571461026c5780633e413bee1461027f5780633fc8cef3146102a657600080fd5b80630e2789bb146101df5780631959df06146102235780631a68650214610238575b600080fd5b6102067f000000000000000000000000f2fed4a7a8d1cc2db73ae0439e07db21d0d58bd981565b6040516001600160a01b0390911681526020015b60405180910390f35b61022b6105f9565b60405161021a9190613a0d565b600554610206906001600160a01b031681565b61025e610259366004613a66565b6106d9565b60405190815260200161021a565b61025e61027a366004613aa7565b610952565b6102067f00000000000000000000000006efdbff2a14a7c8e15944d1f4a48f9f95f663a481565b6102067f000000000000000000000000530000000000000000000000000000000000000481565b600454610206906001600160a01b031681565b6102f36102ee366004613ae0565b61097f565b6040805192835260208301919091520161021a565b61031b610316366004613b39565b610e74565b005b61036f61032b366004613b65565b60026020526000908152604090205467ffffffffffffffff808216916801000000000000000081048216917001000000000000000000000000000000009091041683565b6040805167ffffffffffffffff9485168152928416602084015292169181019190915260600161021a565b6102067f0000000000000000000000000ac992636f863504039a028a6e9a2c192529c48781565b6005546103e69074010000000000000000000000000000000000000000900460ff1681565b604051901515815260200161021a565b610474610404366004613b65565b6040805160608082018352600080835260208084018290529284018190529384526002825292829020825193840183525467ffffffffffffffff80821685526801000000000000000082048116928501929092527001000000000000000000000000000000009004169082015290565b60408051825167ffffffffffffffff9081168252602080850151821690830152928201519092169082015260600161021a565b61031b610f69565b61025e6104bd366004613b7e565b610f7d565b6104d56104d0366004613bcf565b6110fe565b6040805193845260208401929092529082015260600161021a565b61031b61152c565b6003546001600160a01b0316610206565b6102067f000000000000000000000000530000000000000000000000000000000000000481565b61025e61053e366004613b65565b60016020526000908152604090205481565b6102067f0000000000000000000000004dfa880a85e173aaf83cd70f48213aaab369176881565b61031b6118d4565b61025e61058d366004613c31565b611ad2565b61031b611b8b565b6102f36105a8366004613aa7565b611d73565b61031b6105bb366004613aa7565b611dee565b61025e6105ce366004613b7e565b611e46565b61031b6105e1366004613c96565b611ee3565b61031b6105f4366004613b39565b611f73565b606060007f0000000000000000000000000ac992636f863504039a028a6e9a2c192529c4876001600160a01b031663434798856040518163ffffffff1660e01b8152600401600060405180830381865afa15801561065b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106839190810190613ce2565b9050805167ffffffffffffffff81111561069f5761069f613cb3565b6040519080825280602002602001820160405280156106c8578160200160208202803683370190505b5091506106d581836120ee565b5090565b6005546000906001600160a01b031633146107875760405162461bcd60e51b815260206004820152604960248201527f506f6f6c732e6465706f7369745a61705377617057697468647261772069732060448201527f6f6e6c792063616c6c61626c652066726f6d20746865204c697175696469747960648201527f20636f6e74726163740000000000000000000000000000000000000000000000608482015260a4015b60405180910390fd5b61079c6001600160a01b03851633308561228e565b6000806107a98686612345565b6000828152600660205260409020919350915081156108815780546107f69060649070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16613dfe565b6fffffffffffffffffffffffffffffffff16851061087c5760405162461bcd60e51b815260206004820152602760248201527f43616e6e6f74207a6170206d6f7265207468616e203125206f6620746865207260448201527f6573657276657300000000000000000000000000000000000000000000000000606482015260840161077e565b610927565b80546108a1906064906fffffffffffffffffffffffffffffffff16613dfe565b6fffffffffffffffffffffffffffffffff1685106109275760405162461bcd60e51b815260206004820152602760248201527f43616e6e6f74207a6170206d6f7265207468616e203125206f6620746865207260448201527f6573657276657300000000000000000000000000000000000000000000000000606482015260840161077e565b61093281838761241f565b93506109486001600160a01b03871633866125c4565b5050509392505050565b6001600160a01b038083166000908152600760209081526040808320938516835292905220545b92915050565b60008061098a61260d565b6005546001600160a01b03163314610a305760405162461bcd60e51b815260206004820152604260248201527f506f6f6c732e72656d6f76654c6971756964697479206973206f6e6c7920636160448201527f6c6c61626c652066726f6d20746865204c697175696469747920636f6e74726160648201527f6374000000000000000000000000000000000000000000000000000000000000608482015260a40161077e565b60008611610aa65760405162461bcd60e51b815260206004820152602e60248201527f54686520616d6f756e74206f66206c6971756964697479546f52656d6f76652060448201527f63616e6e6f74206265207a65726f000000000000000000000000000000000000606482015260840161077e565b600080610ab38a8a612345565b60008281526006602052604090208054929450909250906fffffffffffffffffffffffffffffffff70010000000000000000000000000000000082048116911611610b6e5780548690610b19908b906fffffffffffffffffffffffffffffffff16613e2d565b610b239190613e44565b81549095506fffffffffffffffffffffffffffffffff80821691610b5d918891700100000000000000000000000000000000900416613e2d565b610b679190613e44565b9350610bf3565b80548690610ba3908b9070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16613e2d565b610bad9190613e44565b81549094506fffffffffffffffffffffffffffffffff7001000000000000000000000000000000008204811691610be691879116613e2d565b610bf09190613e44565b94505b805485908290600090610c199084906fffffffffffffffffffffffffffffffff16613e58565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550838160000160108282829054906101000a90046fffffffffffffffffffffffffffffffff16610c7e9190613e58565b82546101009290920a6fffffffffffffffffffffffffffffffff8181021990931691831602179091558254606491161080159150610ce55750805460647001000000000000000000000000000000009091046fffffffffffffffffffffffffffffffff1610155b610d575760405162461bcd60e51b815260206004820152602d60248201527f496e73756666696369656e74207265736572766573206166746572206c69717560448201527f69646974792072656d6f76616c00000000000000000000000000000000000000606482015260840161077e565b8115610d61579293925b878510158015610d715750868410155b610de35760405162461bcd60e51b815260206004820152602760248201527f496e73756666696369656e7420756e6465726c79696e6720746f6b656e73207260448201527f657475726e656400000000000000000000000000000000000000000000000000606482015260840161077e565b610df76001600160a01b038c1633876125c4565b610e0b6001600160a01b038b1633866125c4565b60408051868152602081018690529081018a90526001600160a01b03808c1691908d16907fbc7d19d505c7ec4db83f3b51f19fb98c4c8a99922e7839d1ee608dfbee29501b9060600160405180910390a3505050610e696001600055565b965096945050505050565b610e7c61260d565b60648111610ecc5760405162461bcd60e51b815260206004820152601860248201527f4465706f73697420616d6f756e7420746f6f20736d616c6c0000000000000000604482015260640161077e565b3360009081526007602090815260408083206001600160a01b038616845290915281208054839290610eff908490613e88565b90915550610f1a90506001600160a01b03831633308461228e565b6040518181526001600160a01b0383169033907f98c09d9949722bae4bd0d988d4050091c3ae7ec6d51d3c6bbfe4233593944e9e906020015b60405180910390a3610f656001600055565b5050565b610f71612666565b610f7b60006126c0565b565b6000610f8761260d565b8180421115610fd85760405162461bcd60e51b815260206004820152600a60248201527f5458204558504952454400000000000000000000000000000000000000000000604482015260640161077e565b3360009081526007602090815260408083206001600160a01b038b16845291829052909120548611156110735760405162461bcd60e51b815260206004820152603560248201527f496e73756666696369656e74206465706f736974656420746f6b656e2062616c60448201527f616e6365206f6620696e697469616c20746f6b656e0000000000000000000000606482015260840161077e565b6001600160a01b0388166000908152602082905260408120805488929061109b908490613e9b565b909155506110ad90508888888861272a565b925082816000896001600160a01b03166001600160a01b0316815260200190815260200160002060008282546110e39190613e88565b90915550506001600055506110f59050565b95945050505050565b600080600061110b61260d565b6005546001600160a01b0316331461118b5760405162461bcd60e51b815260206004820152603f60248201527f506f6f6c732e6164644c6971756964697479206973206f6e6c792063616c6c6160448201527f626c652066726f6d20746865204c697175696469747920636f6e747261637400606482015260840161077e565b60055474010000000000000000000000000000000000000000900460ff166111f55760405162461bcd60e51b815260206004820152601c60248201527f5468652065786368616e6765206973206e6f7420796574206c69766500000000604482015260640161077e565b886001600160a01b03168a6001600160a01b03160361127c5760405162461bcd60e51b815260206004820152602960248201527f43616e6e6f7420616464206c697175696469747920666f72206475706c69636160448201527f746520746f6b656e730000000000000000000000000000000000000000000000606482015260840161077e565b606488116112f25760405162461bcd60e51b815260206004820152602860248201527f54686520616d6f756e74206f6620746f6b656e4120746f20616464206973207460448201527f6f6f20736d616c6c000000000000000000000000000000000000000000000000606482015260840161077e565b606487116113685760405162461bcd60e51b815260206004820152602860248201527f54686520616d6f756e74206f6620746f6b656e4220746f20616464206973207460448201527f6f6f20736d616c6c000000000000000000000000000000000000000000000000606482015260840161077e565b6000806113758c8c612345565b9150915080156113985761138b828a8c89612911565b90965090945092506113ac565b6113a4828b8b89612911565b919650945092505b878510156114225760405162461bcd60e51b815260206004820152602660248201527f496e73756666696369656e7420746f6b656e4120616464656420746f206c697160448201527f7569646974790000000000000000000000000000000000000000000000000000606482015260840161077e565b868410156114985760405162461bcd60e51b815260206004820152602660248201527f496e73756666696369656e7420746f6b656e4220616464656420746f206c697160448201527f7569646974790000000000000000000000000000000000000000000000000000606482015260840161077e565b6114ad6001600160a01b038d1633308861228e565b6114c26001600160a01b038c1633308761228e565b60408051868152602081018690529081018490526001600160a01b03808d1691908e16907f4a1a2a6176e9646d9e3157f7c2ab3c499f18337c0b0828cfb28e0a61de4a11f79060600160405180910390a3505061151f6001600055565b9750975097945050505050565b60007f0000000000000000000000000ac992636f863504039a028a6e9a2c192529c4876001600160a01b031663434798856040518163ffffffff1660e01b8152600401600060405180830381865afa15801561158c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115b49190810190613ce2565b905060005b8151811015610f655760008282815181106115d6576115d6613eae565b602002602001015190506000807f0000000000000000000000000ac992636f863504039a028a6e9a2c192529c4876001600160a01b031663c2fb9aac846040518263ffffffff1660e01b815260040161163191815260200190565b6040805180830381865afa15801561164d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116719190613edd565b915091507f00000000000000000000000053000000000000000000000000000000000000046001600160a01b0316826001600160a01b0316141580156116e957507f00000000000000000000000053000000000000000000000000000000000000046001600160a01b0316816001600160a01b031614155b156118c957600061171b7f00000000000000000000000053000000000000000000000000000000000000048488612c6b565b9050600061172a848489612c6b565b90506000611759847f00000000000000000000000053000000000000000000000000000000000000048a612c6b565b6000878152600260209081526040918290208251606081018452905467ffffffffffffffff8082168084526801000000000000000083048216948401949094527001000000000000000000000000000000009091048116938201939093529293509085161415806117e25750806020015167ffffffffffffffff168367ffffffffffffffff1614155b806118055750806040015167ffffffffffffffff168267ffffffffffffffff1614155b156118c4576040805160608101825267ffffffffffffffff8087168252858116602080840191825286831684860190815260008d8152600290925294902092518354915194518316700100000000000000000000000000000000027fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff95841668010000000000000000027fffffffffffffffffffffffffffffffff00000000000000000000000000000000909316919093161717929092169190911790555b505050505b5050506001016115b9565b7f000000000000000000000000f2fed4a7a8d1cc2db73ae0439e07db21d0d58bd96001600160a01b0316632cf4704a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611932573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119569190613f0c565b6001600160a01b0316336001600160a01b031614611a025760405162461bcd60e51b815260206004820152604860248201527f506f6f6c53746174732e636c65617250726f66697473466f72506f6f6c73206960448201527f73206f6e6c792063616c6c61626c652066726f6d207468652055706b6565702060648201527f636f6e7472616374000000000000000000000000000000000000000000000000608482015260a40161077e565b60007f0000000000000000000000000ac992636f863504039a028a6e9a2c192529c4876001600160a01b031663434798856040518163ffffffff1660e01b8152600401600060405180830381865afa158015611a62573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a8a9190810190613ce2565b905060005b8151811015610f65576001806000848481518110611aaf57611aaf613eae565b602090810291909101810151825281019190915260400160002055600101611a8f565b6000611adc61260d565b8180421115611b2d5760405162461bcd60e51b815260206004820152600a60248201527f5458204558504952454400000000000000000000000000000000000000000000604482015260640161077e565b611b426001600160a01b03891633308861228e565b6000611b51898988600061272a565b9050611b5f8888838861272a565b9250611b756001600160a01b03881633856125c4565b5050611b816001600055565b9695505050505050565b611b9361260d565b7f000000000000000000000000f2fed4a7a8d1cc2db73ae0439e07db21d0d58bd96001600160a01b03166395645e346040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bf1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c159190613f0c565b6001600160a01b031663bd1d4b286040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c769190613f0c565b6001600160a01b0316336001600160a01b031614611d225760405162461bcd60e51b815260206004820152605060248201527f506f6f6c732e737461727445786368616e6765417070726f7665642063616e2060448201527f6f6e6c792062652063616c6c65642066726f6d2074686520426f6f747374726160648201527f7042616c6c6f7420636f6e747261637400000000000000000000000000000000608482015260a40161077e565b611d2a61152c565b600580547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000179055610f7b6001600055565b600080600080611d838686612345565b6000828152600660209081526040918290208251808401909352546fffffffffffffffffffffffffffffffff808216808552700100000000000000000000000000000000909204169183018290529750955091935091508115611de4579293925b5050509250929050565b611df6612666565b600480546001600160a01b038085167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179092556005805492841692909116919091179055610f65610f69565b6000611e5061260d565b8180421115611ea15760405162461bcd60e51b815260206004820152600a60248201527f5458204558504952454400000000000000000000000000000000000000000000604482015260640161077e565b611eb66001600160a01b03881633308861228e565b611ec28787878761272a565b9150611ed86001600160a01b03871633846125c4565b506110f56001600055565b611eeb612666565b6001600160a01b038116611f675760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161077e565b611f70816126c0565b50565b611f7b61260d565b3360009081526007602090815260408083206001600160a01b03861684529091529020548111156120145760405162461bcd60e51b815260206004820152603160248201527f496e73756666696369656e742062616c616e636520746f20776974686472617760448201527f2073706563696669656420616d6f756e74000000000000000000000000000000606482015260840161077e565b606481116120645760405162461bcd60e51b815260206004820152601960248201527f576974686472617720616d6f756e7420746f6f20736d616c6c00000000000000604482015260640161077e565b3360009081526007602090815260408083206001600160a01b038616845290915281208054839290612097908490613e9b565b909155506120b190506001600160a01b03831633836125c4565b6040518181526001600160a01b0383169033907f42856d0378dde02337bb59ae41747abc77ded8ebdbbc5cbdd1e53693b755493890602001610f53565b60005b825181101561228957600083828151811061210e5761210e613eae565b6020026020010151905060006003600160008481526020019081526020016000205461213a9190613e44565b9050801561227f576000828152600260209081526040918290208251606081018452905467ffffffffffffffff80821680845268010000000000000000830482169484019490945270010000000000000000000000000000000090910481169382019390935291146121df578185826000015167ffffffffffffffff16815181106121c7576121c7613eae565b602002602001018181516121db9190613e88565b9052505b602081015167ffffffffffffffff9081161461222e578185826020015167ffffffffffffffff168151811061221657612216613eae565b6020026020010181815161222a9190613e88565b9052505b604081015167ffffffffffffffff9081161461227d578185826040015167ffffffffffffffff168151811061226557612265613eae565b602002602001018181516122799190613e88565b9052505b505b50506001016120f1565b505050565b6040516001600160a01b038085166024830152831660448201526064810182905261233f9085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152612ccc565b50505050565b600080836001600160a01b0316836001600160a01b031610156123bf576040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606085811b8216602084015286901b16603482015260480160405160208183030381529060405280519060200120600191509150612418565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086811b8216602084015285901b166034820152604801604051602081830303815290604052805190602001206000915091505b9250929050565b82546000906fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041684156124885761245e8482613e88565b90508061246b8584613e2d565b6124759190613e44565b92506124818383613e9b565b91506124b8565b6124928483613e88565b91508161249f8583613e2d565b6124a99190613e44565b92506124b58382613e9b565b90505b6064821180156124c85750606481115b6125145760405162461bcd60e51b815260206004820181905260248201527f496e73756666696369656e742072657365727665732061667465722073776170604482015260640161077e565b6fffffffffffffffffffffffffffffffff821180159061254457506fffffffffffffffffffffffffffffffff8111155b6125905760405162461bcd60e51b815260206004820152601c60248201527f5265736572766573206f766572666c6f77206166746572207377617000000000604482015260640161077e565b6fffffffffffffffffffffffffffffffff908116700100000000000000000000000000000000029116179093555090919050565b6040516001600160a01b0383166024820152604481018290526122899084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064016122db565b60026000540361265f5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161077e565b6002600055565b6003546001600160a01b03163314610f7b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161077e565b600380546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60008060006127398787612345565b600082815260066020526040902080549294509092509060646fffffffffffffffffffffffffffffffff90911611801561279b5750805460647001000000000000000000000000000000009091046fffffffffffffffffffffffffffffffff16115b61280d5760405162461bcd60e51b815260206004820152602160248201527f496e73756666696369656e74207265736572766573206265666f72652073776160448201527f7000000000000000000000000000000000000000000000000000000000000000606482015260840161077e565b61281881838861241f565b9350848410156128905760405162461bcd60e51b815260206004820152602360248201527f496e73756666696369656e7420726573756c74696e6720746f6b656e20616d6f60448201527f756e740000000000000000000000000000000000000000000000000000000000606482015260840161077e565b60008061289d8a8a612db4565b9150915060006128ad8383612ffc565b604080518b8152602081018a90529081018290529091506001600160a01b03808c1691908d169033907f1195a8db0f3ef828b684c3193db62af33dae2817d69fa75433df4c896c00be189060600160405180910390a4505050505050949350505050565b60008481526006602052604081208054829182916fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041681158061295a575080155b15612a3c578254899084906000906129859084906fffffffffffffffffffffffffffffffff16613f29565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550878360000160108282829054906101000a90046fffffffffffffffffffffffffffffffff166129ea9190613f29565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055508888898b612a2e9190613e88565b955095509550505050612c61565b600082612a49838c613e2d565b612a539190613e44565b905088811115612a7c5781612a68848b613e2d565b612a729190613e44565b9650889550612a83565b8996508095505b60648711612af95760405162461bcd60e51b815260206004820152602a60248201527f4164646564206c697175696469747920666f7220746f6b656e2030206c65737360448201527f207468616e204455535400000000000000000000000000000000000000000000606482015260840161077e565b60648611612b6f5760405162461bcd60e51b815260206004820152602a60248201527f4164646564206c697175696469747920666f7220746f6b656e2031206c65737360448201527f207468616e204455535400000000000000000000000000000000000000000000606482015260840161077e565b835487908590600090612b959084906fffffffffffffffffffffffffffffffff16613f29565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550858460000160108282829054906101000a90046fffffffffffffffffffffffffffffffff16612bfa9190613f29565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055508183612c3c9190613e88565b612c468789613e88565b612c50908a613e2d565b612c5a9190613e44565b9450505050505b9450945094915050565b600080612c7885856131d5565b905060005b8351811015612cb757838181518110612c9857612c98613eae565b60200260200101518203612caf579150612cc59050565b600101612c7d565b5067ffffffffffffffff9150505b9392505050565b6000612d21826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166132a49092919063ffffffff16565b9050805160001480612d42575080806020019051810190612d429190613f52565b6122895760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161077e565b6000807f0000000000000000000000004dfa880a85e173aaf83cd70f48213aaab36917686001600160a01b0316846001600160a01b031603612e73577f00000000000000000000000053000000000000000000000000000000000000046001600160a01b0316836001600160a01b031603612e7357507f0000000000000000000000004dfa880a85e173aaf83cd70f48213aaab369176890507f00000000000000000000000006efdbff2a14a7c8e15944d1f4a48f9f95f663a4612418565b7f00000000000000000000000053000000000000000000000000000000000000046001600160a01b0316846001600160a01b031603612f2f577f0000000000000000000000004dfa880a85e173aaf83cd70f48213aaab36917686001600160a01b0316836001600160a01b031603612f2f57507f00000000000000000000000006efdbff2a14a7c8e15944d1f4a48f9f95f663a490507f0000000000000000000000004dfa880a85e173aaf83cd70f48213aaab3691768612418565b7f00000000000000000000000053000000000000000000000000000000000000046001600160a01b0316846001600160a01b031603612f9257507f0000000000000000000000004dfa880a85e173aaf83cd70f48213aaab3691768905081612418565b7f00000000000000000000000053000000000000000000000000000000000000046001600160a01b0316836001600160a01b031603612ff557508290507f0000000000000000000000004dfa880a85e173aaf83cd70f48213aaab3691768612418565b5090929050565b60008060008060008060008060006130347f00000000000000000000000053000000000000000000000000000000000000048c612345565b60008281526006602052604090208054929a5090985094506fffffffffffffffffffffffffffffffff80821691700100000000000000000000000000000000900416881561307e57905b6130888d8d612345565b60008281526006602052604090208054929c5090995095506fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041689156130d257905b6130fc8e7f0000000000000000000000005300000000000000000000000000000000000004612345565b60008281526006602052604090208054929e50909a5096506fffffffffffffffffffffffffffffffff808216917001000000000000000000000000000000009004168a1561314657905b6064861180156131565750606485115b80156131625750606484115b801561316e5750606483115b801561317a5750606482115b80156131865750606481115b1561319c576131998686868686866132bb565b96505b50505050505060008111156131c7576131ba818585858b8b8b6133e2565b98506131c78b8b8b61354f565b505050505050505092915050565b6000826001600160a01b0316826001600160a01b0316101561324a576040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084811b8216602084015285901b166034820152604801604051602081830303815290604052805190602001209050610979565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606085811b8216602084015284901b16603482015260480160405160208183030381529060405280519060200120905092915050565b60606132b3848460008561358b565b949350505050565b6000806132cc8887868a898861367d565b905080156132ed5796871c9695861c9594851c9493841c9392831c9291821c915b8786028402878602840281811161330a5760009350505050611b81565b868902888a01870201600061331e836136b8565b613327856136b8565b029050818482038161333b5761333b613da0565b0495508560000361335457600095505050505050611b81565b8415613377579a841b9a99841b9998841b9897841b9796841b9695841b9594841b945b6000868d01878d028161338c5761338c613da0565b049050808b01818b02816133a2576133a2613da0565b049050808901818902816133b8576133b8613da0565b049050868110156133d25760009650505050505050611b81565b5050505050509695505050505050565b6000806133f088868b61241f565b90506133fd87858361241f565b905061340a86848361241f565b90506134168982613e9b565b91506000806134657f00000000000000000000000053000000000000000000000000000000000000047f0000000000000000000000004dfa880a85e173aaf83cd70f48213aaab3691768612345565b600082815260066020526040902080549294509092509060646fffffffffffffffffffffffffffffffff9091161180156134c75750805460647001000000000000000000000000000000009091046fffffffffffffffffffffffffffffffff16115b156135405760006134d982848861241f565b6004546001600160a01b0390811660009081526007602090815260408083207f0000000000000000000000004dfa880a85e173aaf83cd70f48213aaab3691768909416835292905290812080549293508392909190613539908490613e88565b9091555050505b50505050979650505050505050565b600061355b84846131d5565b9050816001600083815260200190815260200160002060008282546135809190613e88565b909155505050505050565b6060824710156136035760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161077e565b600080866001600160a01b0316858760405161361f9190613f98565b60006040518083038185875af1925050503d806000811461365c576040519150601f19603f3d011682016040523d82523d6000602084013e613661565b606091505b5091509150613672878383876137a0565b979650505050505050565b60008061368b888888613819565b9050600061369a868686613819565b90508082116136a957806136ab565b815b9998505050505050505050565b6000816000036136ca57506000919050565b600060016136d78461385c565b901c6001901b905060018184816136f0576136f0613da0565b048201901c9050600181848161370857613708613da0565b048201901c9050600181848161372057613720613da0565b048201901c9050600181848161373857613738613da0565b048201901c9050600181848161375057613750613da0565b048201901c9050600181848161376857613768613da0565b048201901c9050600181848161378057613780613da0565b048201901c9050612cc58182858161379a5761379a613da0565b046138f0565b6060831561380f578251600003613808576001600160a01b0385163b6138085760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161077e565b50816132b3565b6132b38383613906565b60008061382583613930565b61382e85613930565b61383787613930565b0101905060f081101561384e576000915050612cc5565b6110f560f0820360036139d6565b600080608083901c1561387157608092831c92015b604083901c1561388357604092831c92015b602083901c1561389557602092831c92015b601083901c156138a757601092831c92015b600883901c156138b957600892831c92015b600483901c156138cb57600492831c92015b600283901c156138dd57600292831c92015b600183901c156109795760010192915050565b60008183106138ff5781612cc5565b5090919050565b8151156139165781518083602001fd5b8060405162461bcd60e51b815260040161077e9190613fb4565b6000700100000000000000000000000000000000821061395257608091821c91015b68010000000000000000821061396a57604091821c91015b640100000000821061397e57602091821c91015b62010000821061399057601091821c91015b61010082106139a157600891821c91015b601082106139b157600491821c91015b600482106139c157600291821c91015b600282106139d157600191821c91015b919050565b60008215613a0457816139ea600185613e9b565b6139f49190613e44565b6139ff906001613e88565b612cc5565b50600092915050565b6020808252825182820181905260009190848201906040850190845b81811015613a4557835183529284019291840191600101613a29565b50909695505050505050565b6001600160a01b0381168114611f7057600080fd5b600080600060608486031215613a7b57600080fd5b8335613a8681613a51565b92506020840135613a9681613a51565b929592945050506040919091013590565b60008060408385031215613aba57600080fd5b8235613ac581613a51565b91506020830135613ad581613a51565b809150509250929050565b60008060008060008060c08789031215613af957600080fd5b8635613b0481613a51565b95506020870135613b1481613a51565b95989597505050506040840135936060810135936080820135935060a0909101359150565b60008060408385031215613b4c57600080fd5b8235613b5781613a51565b946020939093013593505050565b600060208284031215613b7757600080fd5b5035919050565b600080600080600060a08688031215613b9657600080fd5b8535613ba181613a51565b94506020860135613bb181613a51565b94979496505050506040830135926060810135926080909101359150565b600080600080600080600060e0888a031215613bea57600080fd5b8735613bf581613a51565b96506020880135613c0581613a51565b96999698505050506040850135946060810135946080820135945060a0820135935060c0909101359150565b60008060008060008060c08789031215613c4a57600080fd5b8635613c5581613a51565b95506020870135613c6581613a51565b94506040870135613c7581613a51565b959894975094956060810135955060808101359460a0909101359350915050565b600060208284031215613ca857600080fd5b8135612cc581613a51565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020808385031215613cf557600080fd5b825167ffffffffffffffff80821115613d0d57600080fd5b818501915085601f830112613d2157600080fd5b815181811115613d3357613d33613cb3565b8060051b604051601f19603f83011681018181108582111715613d5857613d58613cb3565b604052918252848201925083810185019188831115613d7657600080fd5b938501935b82851015613d9457845184529385019392850192613d7b565b98975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006fffffffffffffffffffffffffffffffff80841680613e2157613e21613da0565b92169190910492915050565b808202811582820484141761097957610979613dcf565b600082613e5357613e53613da0565b500490565b6fffffffffffffffffffffffffffffffff828116828216039080821115613e8157613e81613dcf565b5092915050565b8082018082111561097957610979613dcf565b8181038181111561097957610979613dcf565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008060408385031215613ef057600080fd5b8251613efb81613a51565b6020840151909250613ad581613a51565b600060208284031215613f1e57600080fd5b8151612cc581613a51565b6fffffffffffffffffffffffffffffffff818116838216019080821115613e8157613e81613dcf565b600060208284031215613f6457600080fd5b81518015158114612cc557600080fd5b60005b83811015613f8f578181015183820152602001613f77565b50506000910152565b60008251613faa818460208701613f74565b9190910192915050565b6020815260008251806020840152613fd3816040850160208701613f74565b601f01601f1916919091016040019291505056fea2646970667358221220f3d4061a98a4f87ea126f9cf151a278e3aec480c7d5dff3a7e6fc65d66181d4d64736f6c63430008160033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000f2fed4a7a8d1cc2db73ae0439e07db21d0d58bd90000000000000000000000000ac992636f863504039a028a6e9a2c192529c487
-----Decoded View---------------
Arg [0] : _exchangeConfig (address): 0xF2fEd4a7a8D1CC2dB73AE0439E07db21D0D58bD9
Arg [1] : _poolsConfig (address): 0x0aC992636f863504039A028A6E9a2c192529c487
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000f2fed4a7a8d1cc2db73ae0439e07db21d0d58bd9
Arg [1] : 0000000000000000000000000ac992636f863504039a028a6e9a2c192529c487
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 27 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
SCROLL | 100.00% | $1 | 388.1058 | $388.11 |
[ 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.