More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 34,705 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Finalize Redempt... | 16625193 | 18 hrs ago | IN | 0 ETH | 0.00000026 | ||||
Redeem | 16625183 | 18 hrs ago | IN | 0.0003 ETH | 0.00000032 | ||||
Finalize Redempt... | 16531865 | 2 days ago | IN | 0 ETH | 0.00000001 | ||||
Redeem | 16531854 | 2 days ago | IN | 0.0003 ETH | 0.00000003 | ||||
Finalize Redempt... | 16486183 | 3 days ago | IN | 0 ETH | 0.00000024 | ||||
Redeem | 16486173 | 3 days ago | IN | 0.0003 ETH | 0.00000029 | ||||
Finalize Redempt... | 16479491 | 4 days ago | IN | 0 ETH | 0.00000023 | ||||
Redeem | 16479479 | 4 days ago | IN | 0.0003 ETH | 0.00000028 | ||||
Finalize Redempt... | 16479469 | 4 days ago | IN | 0 ETH | 0.00000011 | ||||
Redeem | 16479459 | 4 days ago | IN | 0.0003 ETH | 0.00000023 | ||||
Finalize Redempt... | 16478283 | 4 days ago | IN | 0 ETH | 0.00000014 | ||||
Redeem | 16478274 | 4 days ago | IN | 0.0003 ETH | 0.00000025 | ||||
Finalize Redempt... | 16478003 | 4 days ago | IN | 0 ETH | 0.00000003 | ||||
Redeem | 16477991 | 4 days ago | IN | 0.0003 ETH | 0.00000004 | ||||
Finalize Redempt... | 16458259 | 4 days ago | IN | 0 ETH | 0.00000005 | ||||
Redeem | 16458251 | 4 days ago | IN | 0.0003 ETH | 0.00000006 | ||||
Finalize Redempt... | 16451237 | 4 days ago | IN | 0 ETH | 0.00000022 | ||||
Redeem | 16451225 | 4 days ago | IN | 0.0003 ETH | 0.00000027 | ||||
Finalize Redempt... | 16451168 | 4 days ago | IN | 0 ETH | 0.00000022 | ||||
Redeem | 16451157 | 4 days ago | IN | 0.0003 ETH | 0.00000027 | ||||
Finalize Redempt... | 16428944 | 5 days ago | IN | 0 ETH | 0.00000002 | ||||
Redeem | 16428936 | 5 days ago | IN | 0.0003 ETH | 0.00000004 | ||||
Finalize Redempt... | 16428108 | 5 days ago | IN | 0 ETH | 0.00000005 | ||||
Redeem | 16428099 | 5 days ago | IN | 0.0003 ETH | 0.00000006 | ||||
Finalize Redempt... | 16397192 | 6 days ago | IN | 0 ETH | 0.00000025 |
Latest 25 internal transactions (View All)
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
16625193 | 18 hrs ago | 0.19999999 ETH | ||||
16625193 | 18 hrs ago | 0.19999999 ETH | ||||
16625193 | 18 hrs ago | 0.0003 ETH | ||||
16531865 | 2 days ago | 0.0003 ETH | ||||
16486183 | 3 days ago | 0.10177062 ETH | ||||
16486183 | 3 days ago | 0.10177062 ETH | ||||
16486183 | 3 days ago | 0.0003 ETH | ||||
16479491 | 4 days ago | 0.00427829 ETH | ||||
16479491 | 4 days ago | 0.00427829 ETH | ||||
16479491 | 4 days ago | 0.0003 ETH | ||||
16479469 | 4 days ago | 0.0003 ETH | ||||
16478283 | 4 days ago | 0.0003 ETH | ||||
16478003 | 4 days ago | 310.43276958 ETH | ||||
16478003 | 4 days ago | 310.43276958 ETH | ||||
16478003 | 4 days ago | 0.0003 ETH | ||||
16458259 | 4 days ago | 0.820645 ETH | ||||
16458259 | 4 days ago | 0.820645 ETH | ||||
16458259 | 4 days ago | 0.0003 ETH | ||||
16451237 | 4 days ago | 0.07648946 ETH | ||||
16451237 | 4 days ago | 0.07648946 ETH | ||||
16451237 | 4 days ago | 0.0003 ETH | ||||
16451168 | 4 days ago | 0.22805492 ETH | ||||
16451168 | 4 days ago | 0.22805492 ETH | ||||
16451168 | 4 days ago | 0.0003 ETH | ||||
16446911 | 4 days ago | 0.00994998 ETH |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
LiquidityPoolRouter
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 888888 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; import {ITransferManager} from "@looksrare/contracts-transfer-manager/contracts/interfaces/ITransferManager.sol"; import {OwnableTwoSteps} from "@looksrare/contracts-libs/contracts/OwnableTwoSteps.sol"; import {LowLevelWETH} from "@looksrare/contracts-libs/contracts/lowLevelCallers/LowLevelWETH.sol"; import {LowLevelERC20Transfer} from "@looksrare/contracts-libs/contracts/lowLevelCallers/LowLevelERC20Transfer.sol"; import {IWETH} from "@looksrare/contracts-libs/contracts/interfaces/generic/IWETH.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {ReentrancyGuard} from "@looksrare/contracts-libs/contracts/ReentrancyGuard.sol"; import {Pausable} from "@looksrare/contracts-libs/contracts/Pausable.sol"; import {ERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; import {IBlast, YieldMode as IBlast__YieldMode, GasMode as IBlast__GasMode} from "./interfaces/IBlast.sol"; import {IERC20Rebasing, YieldMode as IERC20Rebasing__YieldMode} from "./interfaces/IERC20Rebasing.sol"; import {IBlastPoints} from "./interfaces/IBlastPoints.sol"; import {LiquidityPool} from "./LiquidityPool.sol"; /** * @title LiquidityPoolRouter * @notice All liquidity pool related operations must be routed through this contract. * @author YOLO Games protocol team */ contract LiquidityPoolRouter is OwnableTwoSteps, LowLevelWETH, LowLevelERC20Transfer, ReentrancyGuard, Pausable { using SafeERC20 for IERC20; event LiquidityPoolRouter__DepositInitialized( address user, address liquidityPool, uint256 amount, uint256 expectedShares, uint256 finalizationIncentive ); event LiquidityPoolRouter__DepositFinalized( address caller, address user, address liquidityPool, uint256 amount, uint256 sharesMinted ); event LiquidityPoolRouter__DepositLimitUpdated( address liquidityPool, uint256 minDepositAmount, uint256 maxDepositAmount, uint256 maxBalance ); event LiquidityPoolRouter__FinalizationParamsUpdated( uint256 timelockDelay, uint256 finalizationForAllDelay, uint256 finalizationIncentive ); event LiquidityPoolRouter__LiquidityPoolAdded(address token, address liquidityPool); event LiquidityPoolRouter__RedemptionInitialized( address user, address liquidityPool, uint256 amount, uint256 expectedAssets, uint256 finalizationIncentive ); event LiquidityPoolRouter__RedemptionFinalized( address caller, address user, address liquidityPool, uint256 amount, uint256 assetsRedeemed ); error LiquidityPoolRouter__DepositAmountTooHigh(); error LiquidityPoolRouter__DepositAmountTooLow(); error LiquidityPoolRouter__FinalizationForAllIsNotOpen(); error LiquidityPoolRouter__FinalizationIncentiveNotPaid(); error LiquidityPoolRouter__FinalizationIncentiveTooHigh(); error LiquidityPoolRouter__FinalizationForAllDelayTooHigh(); error LiquidityPoolRouter__FinalizationForAllDelayTooLow(); error LiquidityPoolRouter__InvalidTimelockDelay(); error LiquidityPoolRouter__MaxDepositAmountTooHigh(); error LiquidityPoolRouter__MinDepositAmountTooHigh(); error LiquidityPoolRouter__NoLiquidityPoolForToken(); error LiquidityPoolRouter__NoOngoingDeposit(); error LiquidityPoolRouter__NoOngoingRedemption(); error LiquidityPoolRouter__OngoingDeposit(); error LiquidityPoolRouter__OngoingRedemption(); error LiquidityPoolRouter__TimelockIsNotOver(); error LiquidityPoolRouter__TokenAlreadyHasLiquidityPool(); error LiquidityPoolRouter__WETHDepositNotAllowed(); error LiquidityPoolRouter__ZeroExpectedAssets(); error LiquidityPoolRouter__ZeroExpectedShares(); /** * @notice Deposit struct * @param liquidityPool The liquidity pool address * @param amount The asset deposit amount * @param expectedShares The expected shares to be minted during initialization * @param initializedAt The timestamp when the deposit was initialized * @param finalizationIncentive The finalization incentive for the deposit */ struct Deposit { address liquidityPool; uint256 amount; uint256 expectedShares; uint256 initializedAt; uint256 finalizationIncentive; } /** * @notice Redemption struct * @param liquidityPool The liquidity pool address * @param shares The shares to be redeemed * @param expectedAssets The expected assets to be redeemed during initialization * @param initializedAt The timestamp when the redemption was initialized * @param finalizationIncentive The finalization incentive for the redemption */ struct Redemption { address liquidityPool; uint256 shares; uint256 expectedAssets; uint256 initializedAt; uint256 finalizationIncentive; } /** * @notice DepositLimit struct * @param minDepositAmount Minimum deposit amount per transaction * @param maxDepositAmount Maximum deposit amount per transaction * @param maxBalance Liquidity pool max balance (only prevents further deposits) */ struct DepositLimit { uint256 minDepositAmount; uint256 maxDepositAmount; uint256 maxBalance; } /** * @notice FinalizationParams struct * * @param timelockDelay Time lock for the 2 steps deposit/withdrawal process * @param finalizationForAllDelay Time lock until a deposit's finalization is open to all * @param finalizationIncentive The router incentivizes bots that backstop finalization if the depositor/redeemer does not complete the process */ struct FinalizationParams { uint80 timelockDelay; uint80 finalizationForAllDelay; uint80 finalizationIncentive; } /** * @notice We charge a fee of 0.5% on deposits. */ uint256 public constant DEPOSIT_FEE_BASIS_POINTS = 50; /** * @notice WETH contract address */ address public immutable WETH; /** * @notice USDB contract address */ address public immutable USDB; /** * @notice Transfer manager contract address */ ITransferManager public immutable TRANSFER_MANAGER; /** * @notice liquidityPools keeps track of supported tokens and their corresponding liquidity pools * * @dev ETH liquidity pool should use the address of WETH as the key */ mapping(address token => address liquidityPool) public liquidityPools; /** * @notice DepositLimit keeps track of the deposit limits for each liquidity pool */ mapping(address liquidityPool => DepositLimit) public depositLimit; /** * @notice deposits keeps track of each address's pending deposit */ mapping(address account => Deposit) public deposits; /** * @notice redemptions keeps track of each address's pending share redemption */ mapping(address account => Redemption) public redemptions; /** * @notice pendingDeposits keeps track of each liquidity pool's pending deposit amount */ mapping(address liquidityPool => uint256 amount) public pendingDeposits; /** * @notice pendingWithdrawals keeps track of each liquidity pool's pending withdrawal amount */ mapping(address liquidityPool => uint256 amount) public pendingWithdrawals; FinalizationParams public finalizationParams; /** * @param _owner The owner of the contract * @param _weth WETH contract address * @param _usdb USDB contract address * @param _transferManager Transfer manager contract address * @param _blast Blast precompile * @param _blastPoints Blast points * @param _blastPointsOperator Blast points operator */ constructor( address _owner, address _weth, address _usdb, address _transferManager, address _blast, address _blastPoints, address _blastPointsOperator ) OwnableTwoSteps(_owner) { WETH = _weth; USDB = _usdb; TRANSFER_MANAGER = ITransferManager(_transferManager); _setFinalizationParams(10 seconds, 5 minutes, 0.0003 ether); IBlast(_blast).configure(IBlast__YieldMode.CLAIMABLE, IBlast__GasMode.CLAIMABLE, _owner); IERC20Rebasing(_weth).configure(IERC20Rebasing__YieldMode.CLAIMABLE); IERC20Rebasing(_usdb).configure(IERC20Rebasing__YieldMode.CLAIMABLE); IBlastPoints(_blastPoints).configurePointsOperator(_blastPointsOperator); } /** * @notice Support a new liquidity pool. Only callable by the owner. * * @param liquidityPool Liquidity pool address */ function addLiquidityPool(address liquidityPool) external onlyOwner { address token = ERC4626(liquidityPool).asset(); if (liquidityPools[token] != address(0)) { revert LiquidityPoolRouter__TokenAlreadyHasLiquidityPool(); } liquidityPools[token] = liquidityPool; emit LiquidityPoolRouter__LiquidityPoolAdded(token, liquidityPool); } /** * @notice Update the deposit limits for a liquidity pool. Only callable by the owner. * * @param liquidityPool The liquidity pool's contract address * @param minDepositAmount The minimum deposit amount per transaction * @param maxDepositAmount The maximum deposit amount per transaction * @param maxBalance The liquidity pool's max balance */ function setDepositLimit( address liquidityPool, uint256 minDepositAmount, uint256 maxDepositAmount, uint256 maxBalance ) external onlyOwner { if (minDepositAmount > maxDepositAmount) { revert LiquidityPoolRouter__MinDepositAmountTooHigh(); } if (maxBalance < maxDepositAmount) { revert LiquidityPoolRouter__MaxDepositAmountTooHigh(); } depositLimit[liquidityPool] = DepositLimit(minDepositAmount, maxDepositAmount, maxBalance); emit LiquidityPoolRouter__DepositLimitUpdated(liquidityPool, minDepositAmount, maxDepositAmount, maxBalance); } /** * @notice Update finalization params. Only callable by the owner. * * @param _timelockDelay The new timelock delay * @param _finalizationForAllDelay The new finalization for all delay * @param _finalizationIncentive The new finalization incentive */ function setFinalizationParams( uint80 _timelockDelay, uint80 _finalizationForAllDelay, uint80 _finalizationIncentive ) external onlyOwner { _setFinalizationParams(_timelockDelay, _finalizationForAllDelay, _finalizationIncentive); } /** * @notice First step of the 2 steps deposit process. Commit ETH into the pool * and estimate the expected amount of shares to be minted. * * @param amount The deposit amount */ function depositETH(uint256 amount) external payable nonReentrant whenNotPaused { address liquidityPool = _getLiquidityPoolOrRevert(WETH); if (amount + finalizationParams.finalizationIncentive != msg.value) { revert LiquidityPoolRouter__FinalizationIncentiveNotPaid(); } uint256 depositFee = (amount * DEPOSIT_FEE_BASIS_POINTS) / 10_000; amount -= depositFee; _validateDepositAmount(liquidityPool, amount); if (deposits[msg.sender].amount != 0) { revert LiquidityPoolRouter__OngoingDeposit(); } _transferETHAndWrapIfFailWithGasLimit(WETH, owner, depositFee, gasleft()); uint256 expectedShares = ERC4626(liquidityPool).previewDeposit(amount); if (expectedShares == 0) { revert LiquidityPoolRouter__ZeroExpectedShares(); } deposits[msg.sender] = Deposit( liquidityPool, amount, expectedShares, block.timestamp, finalizationParams.finalizationIncentive ); pendingDeposits[liquidityPool] += amount; emit LiquidityPoolRouter__DepositInitialized( msg.sender, liquidityPool, amount + depositFee, expectedShares, finalizationParams.finalizationIncentive ); } /** * @notice First step of the 2 steps deposit process. Commit ERC-20 token into the pool * and estimate the expected amount of shares to be minted. * * @param token The deposit token * @param amount The deposit amount */ function deposit(address token, uint256 amount) external payable nonReentrant whenNotPaused { if (token == WETH) { revert LiquidityPoolRouter__WETHDepositNotAllowed(); } address liquidityPool = _getLiquidityPoolOrRevert(token); _validateFinalizationIncentivePayment(); uint256 depositFee = (amount * DEPOSIT_FEE_BASIS_POINTS) / 10_000; amount -= depositFee; _validateDepositAmount(liquidityPool, amount); if (deposits[msg.sender].amount != 0) { revert LiquidityPoolRouter__OngoingDeposit(); } TRANSFER_MANAGER.transferERC20(token, msg.sender, address(this), amount); TRANSFER_MANAGER.transferERC20(token, msg.sender, owner, depositFee); uint256 expectedShares = ERC4626(liquidityPool).previewDeposit(amount); if (expectedShares == 0) { revert LiquidityPoolRouter__ZeroExpectedShares(); } deposits[msg.sender] = Deposit( liquidityPool, amount, expectedShares, block.timestamp, finalizationParams.finalizationIncentive ); pendingDeposits[liquidityPool] += amount; emit LiquidityPoolRouter__DepositInitialized( msg.sender, liquidityPool, amount + depositFee, expectedShares, finalizationParams.finalizationIncentive ); } /** * @notice Finalize a deposit after the first step. Mint shares to the depositor. * If the expected shares are more than the actual shares, just mint the actual shares. * If the expected shares are less than the actual shares, mint the expected shares and * send the remaining ETH back to the liquidity pool. * * @param depositor The depositor address */ function finalizeDeposit(address depositor) external nonReentrant { uint256 amount = deposits[depositor].amount; if (amount == 0) { revert LiquidityPoolRouter__NoOngoingDeposit(); } uint256 initializedAt = deposits[depositor].initializedAt; _validateTimelockIsOver(initializedAt); _validateFinalizationIsOpenForAll(depositor, initializedAt); address payable liquidityPool = payable(deposits[depositor].liquidityPool); address token = ERC4626(liquidityPool).asset(); uint256 expectedShares = deposits[depositor].expectedShares; uint256 actualShares = ERC4626(liquidityPool).previewDeposit(amount); uint256 incentive = deposits[depositor].finalizationIncentive; deposits[depositor] = Deposit(address(0), 0, 0, 0, 0); pendingDeposits[liquidityPool] -= amount; _transferETHAndWrapIfFailWithGasLimit(WETH, msg.sender, incentive, 2_300); uint256 sharesMinted; uint256 amountRequired; if (expectedShares >= actualShares) { amountRequired = amount; sharesMinted = _deposit(token, liquidityPool, amountRequired, depositor); } else { amountRequired = ERC4626(liquidityPool).previewMint(expectedShares); sharesMinted = _deposit(token, liquidityPool, amountRequired, depositor); if (token == WETH) { _transferETHAndWrapIfFailWithGasLimit(WETH, liquidityPool, amount - amountRequired, gasleft()); } else { _executeERC20DirectTransfer(token, liquidityPool, amount - amountRequired); } } emit LiquidityPoolRouter__DepositFinalized(msg.sender, depositor, liquidityPool, amountRequired, sharesMinted); } /** * @notice First step of the 2 steps redemption process. Commit vault shares into the pool * and estimate the expected amount of assets to redeem. * * @param token The token to redeem * @param amount The redemption amount */ function redeem(address token, uint256 amount) external payable nonReentrant { address liquidityPool = _getLiquidityPoolOrRevert(token); _validateFinalizationIncentivePayment(); if (redemptions[msg.sender].shares != 0) { revert LiquidityPoolRouter__OngoingRedemption(); } TRANSFER_MANAGER.transferERC20(liquidityPool, msg.sender, address(this), amount); uint256 expectedAssets = ERC4626(liquidityPool).previewRedeem(amount); if (expectedAssets == 0) { revert LiquidityPoolRouter__ZeroExpectedAssets(); } redemptions[msg.sender] = Redemption( liquidityPool, amount, expectedAssets, block.timestamp, finalizationParams.finalizationIncentive ); pendingWithdrawals[liquidityPool] += expectedAssets; emit LiquidityPoolRouter__RedemptionInitialized( msg.sender, liquidityPool, amount, expectedAssets, finalizationParams.finalizationIncentive ); } /** * @notice Finalize a redemption after the first step. Redeem shares for the redeemer. * If the expected assets are more than the actual assets, send the actual assets to the redeemer. * If the expected assets are less than the actual assets, send the expected shares to the redeemer and * send the remaining ETH back to the liquidity pool. * * @param redeemer The redeemer address */ function finalizeRedemption(address redeemer) external nonReentrant { uint256 amount = redemptions[redeemer].shares; if (amount == 0) { revert LiquidityPoolRouter__NoOngoingRedemption(); } uint256 initializedAt = redemptions[redeemer].initializedAt; _validateTimelockIsOver(initializedAt); _validateFinalizationIsOpenForAll(redeemer, initializedAt); address payable liquidityPool = payable(redemptions[redeemer].liquidityPool); address token = ERC4626(liquidityPool).asset(); uint256 expectedAssets = redemptions[redeemer].expectedAssets; uint256 incentive = redemptions[redeemer].finalizationIncentive; redemptions[redeemer] = Redemption(address(0), 0, 0, 0, 0); pendingWithdrawals[liquidityPool] -= expectedAssets; uint256 assetsRedeemed = LiquidityPool(liquidityPool).redeem(amount, address(this), address(this)); _transferETHAndWrapIfFailWithGasLimit(WETH, msg.sender, incentive, 2_300); if (expectedAssets >= assetsRedeemed) { _transferAssetsRedeemed(token, redeemer, assetsRedeemed); } else { _transferAssetsRedeemed(token, redeemer, expectedAssets); address receiver = LiquidityPool(liquidityPool).totalSupply() == 0 ? owner : liquidityPool; _executeERC20DirectTransfer(token, receiver, assetsRedeemed - expectedAssets); assetsRedeemed = expectedAssets; } emit LiquidityPoolRouter__RedemptionFinalized(msg.sender, redeemer, liquidityPool, amount, assetsRedeemed); } /** * @notice Toggle paused state. Only callable by contract owner. */ function togglePaused() external onlyOwner { paused() ? _unpause() : _pause(); } /** * @notice Claim Blast yield. Only callable by contract owner. * @param receiver Receiver of the yield */ function claimYield(address receiver) external onlyOwner { _claimYield(WETH, receiver); _claimYield(USDB, receiver); } receive() external payable {} /** * @dev Wrap ETH, approve the liquidity pool to spend the wrapped ETH, and deposit the wrapped ETH * * @param token The token address * @param liquidityPool The liquidity pool address * @param amount The deposit amount * @param depositor The depositor address */ function _deposit( address token, address liquidityPool, uint256 amount, address depositor ) private returns (uint256 sharesMinted) { if (token == WETH) { IWETH(WETH).deposit{value: amount}(); } IERC20(token).forceApprove(liquidityPool, amount); sharesMinted = LiquidityPool(liquidityPool).deposit(amount, depositor); } /** * @dev Transfer the assets redeemed to the redeemer * * @param token The token address * @param redeemer The redeemer address * @param assetsRedeemed The assets redeemed */ function _transferAssetsRedeemed(address token, address redeemer, uint256 assetsRedeemed) private { if (token == WETH) { IWETH(WETH).withdraw(assetsRedeemed); _transferETHAndWrapIfFailWithGasLimit(WETH, redeemer, assetsRedeemed, 2_300); } else { _executeERC20DirectTransfer(token, redeemer, assetsRedeemed); } } /** * @notice Claim Blast yield. * * @param token WETH or USDB * @param receiver Receiver of the yield */ function _claimYield(address token, address receiver) private { IERC20Rebasing rebasingAsset = IERC20Rebasing(token); uint256 claimableAmount = rebasingAsset.getClaimableAmount(address(this)); if (claimableAmount != 0) { rebasingAsset.claim(receiver, claimableAmount); } } /** * @dev Update finalization params * * @param _timelockDelay The new timelock delay * @param _finalizationForAllDelay The new finalization for all delay * @param _finalizationIncentive The new finalization incentive */ function _setFinalizationParams( uint80 _timelockDelay, uint80 _finalizationForAllDelay, uint80 _finalizationIncentive ) private { if (_finalizationIncentive > 0.01 ether) { revert LiquidityPoolRouter__FinalizationIncentiveTooHigh(); } if (_timelockDelay < 5 seconds || _timelockDelay > 1 minutes) { revert LiquidityPoolRouter__InvalidTimelockDelay(); } if (_finalizationForAllDelay > 5 minutes) { revert LiquidityPoolRouter__FinalizationForAllDelayTooHigh(); } if (_finalizationIncentive > 0 && _finalizationForAllDelay < 30 seconds) { revert LiquidityPoolRouter__FinalizationForAllDelayTooLow(); } finalizationParams = FinalizationParams(_timelockDelay, _finalizationForAllDelay, _finalizationIncentive); emit LiquidityPoolRouter__FinalizationParamsUpdated( _timelockDelay, _finalizationForAllDelay, _finalizationIncentive ); } /** * @dev Get the liquidity pool address for a token or revert if none is found * * @param token The token address * * @return liquidityPool The liquidity pool address */ function _getLiquidityPoolOrRevert(address token) private view returns (address liquidityPool) { liquidityPool = liquidityPools[token]; if (liquidityPool == address(0)) { revert LiquidityPoolRouter__NoLiquidityPoolForToken(); } } /** * @dev Validate the deposit amount to be within the acceptable range * * @param liquidityPool The liquidity pool address * @param amount The deposit amount */ function _validateDepositAmount(address liquidityPool, uint256 amount) private view { if (amount == 0) { revert LiquidityPoolRouter__DepositAmountTooLow(); } if (amount < depositLimit[liquidityPool].minDepositAmount) { revert LiquidityPoolRouter__DepositAmountTooLow(); } if (amount > depositLimit[liquidityPool].maxDepositAmount) { revert LiquidityPoolRouter__DepositAmountTooHigh(); } if ( IERC20(ERC4626(liquidityPool).asset()).balanceOf(liquidityPool) + amount + pendingDeposits[liquidityPool] > depositLimit[liquidityPool].maxBalance ) { revert LiquidityPoolRouter__DepositAmountTooHigh(); } } /** * @dev Validate that the deposit/redemption finalization is open for all * * @param requester The requester address * @param initializedAt The timestamp when the deposit/redemption was initialized */ function _validateFinalizationIsOpenForAll(address requester, uint256 initializedAt) private view { if ( msg.sender != requester && block.timestamp < initializedAt + finalizationParams.timelockDelay + finalizationParams.finalizationForAllDelay ) { revert LiquidityPoolRouter__FinalizationForAllIsNotOpen(); } } /** * @dev Validate that the timelock is over * * @param initializedAt The timestamp when the deposit/redemption was initialized */ function _validateTimelockIsOver(uint256 initializedAt) private view { if (block.timestamp < initializedAt + finalizationParams.timelockDelay) { revert LiquidityPoolRouter__TimelockIsNotOver(); } } function _validateFinalizationIncentivePayment() private view { if (msg.value != finalizationParams.finalizationIncentive) { revert LiquidityPoolRouter__FinalizationIncentiveNotPaid(); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /** * @notice It is emitted if the call recipient is not a contract. */ error NotAContract();
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /** * @notice It is emitted if the ETH transfer fails. */ error ETHTransferFail(); /** * @notice It is emitted if the ERC20 approval fails. */ error ERC20ApprovalFail(); /** * @notice It is emitted if the ERC20 transfer fails. */ error ERC20TransferFail(); /** * @notice It is emitted if the ERC20 transferFrom fails. */ error ERC20TransferFromFail(); /** * @notice It is emitted if the ERC721 transferFrom fails. */ error ERC721TransferFromFail(); /** * @notice It is emitted if the ERC1155 safeTransferFrom fails. */ error ERC1155SafeTransferFromFail(); /** * @notice It is emitted if the ERC1155 safeBatchTransferFrom fails. */ error ERC1155SafeBatchTransferFromFail();
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; interface IERC20 { event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address to, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function transferFrom(address from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity >=0.5.0; interface IWETH { function deposit() external payable; function transfer(address dst, uint256 wad) external returns (bool); function withdraw(uint256 wad) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /** * @title IOwnableTwoSteps * @author LooksRare protocol team (👀,💎) */ interface IOwnableTwoSteps { /** * @notice This enum keeps track of the ownership status. * @param NoOngoingTransfer The default status when the owner is set * @param TransferInProgress The status when a transfer to a new owner is initialized * @param RenouncementInProgress The status when a transfer to address(0) is initialized */ enum Status { NoOngoingTransfer, TransferInProgress, RenouncementInProgress } /** * @notice This is returned when there is no transfer of ownership in progress. */ error NoOngoingTransferInProgress(); /** * @notice This is returned when the caller is not the owner. */ error NotOwner(); /** * @notice This is returned when there is no renouncement in progress but * the owner tries to validate the ownership renouncement. */ error RenouncementNotInProgress(); /** * @notice This is returned when the transfer is already in progress but the owner tries * initiate a new ownership transfer. */ error TransferAlreadyInProgress(); /** * @notice This is returned when there is no ownership transfer in progress but the * ownership change tries to be approved. */ error TransferNotInProgress(); /** * @notice This is returned when the ownership transfer is attempted to be validated by the * a caller that is not the potential owner. */ error WrongPotentialOwner(); /** * @notice This is emitted if the ownership transfer is cancelled. */ event CancelOwnershipTransfer(); /** * @notice This is emitted if the ownership renouncement is initiated. */ event InitiateOwnershipRenouncement(); /** * @notice This is emitted if the ownership transfer is initiated. * @param previousOwner Previous/current owner * @param potentialOwner Potential/future owner */ event InitiateOwnershipTransfer(address previousOwner, address potentialOwner); /** * @notice This is emitted when there is a new owner. */ event NewOwner(address newOwner); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /** * @title IReentrancyGuard * @author LooksRare protocol team (👀,💎) */ interface IReentrancyGuard { /** * @notice This is returned when there is a reentrant call. */ error ReentrancyFail(); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; // Interfaces import {IERC20} from "../interfaces/generic/IERC20.sol"; // Errors import {ERC20TransferFail, ERC20TransferFromFail} from "../errors/LowLevelErrors.sol"; import {NotAContract} from "../errors/GenericErrors.sol"; /** * @title LowLevelERC20Transfer * @notice This contract contains low-level calls to transfer ERC20 tokens. * @author LooksRare protocol team (👀,💎) */ contract LowLevelERC20Transfer { /** * @notice Execute ERC20 transferFrom * @param currency Currency address * @param from Sender address * @param to Recipient address * @param amount Amount to transfer */ function _executeERC20TransferFrom(address currency, address from, address to, uint256 amount) internal { if (currency.code.length == 0) { revert NotAContract(); } (bool status, bytes memory data) = currency.call(abi.encodeCall(IERC20.transferFrom, (from, to, amount))); if (!status) { revert ERC20TransferFromFail(); } if (data.length > 0) { if (!abi.decode(data, (bool))) { revert ERC20TransferFromFail(); } } } /** * @notice Execute ERC20 (direct) transfer * @param currency Currency address * @param to Recipient address * @param amount Amount to transfer */ function _executeERC20DirectTransfer(address currency, address to, uint256 amount) internal { if (currency.code.length == 0) { revert NotAContract(); } (bool status, bytes memory data) = currency.call(abi.encodeCall(IERC20.transfer, (to, amount))); if (!status) { revert ERC20TransferFail(); } if (data.length > 0) { if (!abi.decode(data, (bool))) { revert ERC20TransferFail(); } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; // Interfaces import {IWETH} from "../interfaces/generic/IWETH.sol"; /** * @title LowLevelWETH * @notice This contract contains a function to transfer ETH with an option to wrap to WETH. * If the ETH transfer fails within a gas limit, the amount in ETH is wrapped to WETH and then transferred. * @author LooksRare protocol team (👀,💎) */ contract LowLevelWETH { /** * @notice It transfers ETH to a recipient with a specified gas limit. * If the original transfers fails, it wraps to WETH and transfers the WETH to recipient. * @param _WETH WETH address * @param _to Recipient address * @param _amount Amount to transfer * @param _gasLimit Gas limit to perform the ETH transfer */ function _transferETHAndWrapIfFailWithGasLimit( address _WETH, address _to, uint256 _amount, uint256 _gasLimit ) internal { bool status; assembly { status := call(_gasLimit, _to, _amount, 0, 0, 0, 0) } if (!status) { IWETH(_WETH).deposit{value: _amount}(); IWETH(_WETH).transfer(_to, _amount); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; // Interfaces import {IOwnableTwoSteps} from "./interfaces/IOwnableTwoSteps.sol"; /** * @title OwnableTwoSteps * @notice This contract offers transfer of ownership in two steps with potential owner * having to confirm the transaction to become the owner. * Renouncement of the ownership is also a two-step process since the next potential owner is the address(0). * @author LooksRare protocol team (👀,💎) */ abstract contract OwnableTwoSteps is IOwnableTwoSteps { /** * @notice Address of the current owner. */ address public owner; /** * @notice Address of the potential owner. */ address public potentialOwner; /** * @notice Ownership status. */ Status public ownershipStatus; /** * @notice Modifier to wrap functions for contracts that inherit this contract. */ modifier onlyOwner() { _onlyOwner(); _; } /** * @notice Constructor * @param _owner The contract's owner */ constructor(address _owner) { owner = _owner; emit NewOwner(_owner); } /** * @notice This function is used to cancel the ownership transfer. * @dev This function can be used for both cancelling a transfer to a new owner and * cancelling the renouncement of the ownership. */ function cancelOwnershipTransfer() external onlyOwner { Status _ownershipStatus = ownershipStatus; if (_ownershipStatus == Status.NoOngoingTransfer) { revert NoOngoingTransferInProgress(); } if (_ownershipStatus == Status.TransferInProgress) { delete potentialOwner; } delete ownershipStatus; emit CancelOwnershipTransfer(); } /** * @notice This function is used to confirm the ownership renouncement. */ function confirmOwnershipRenouncement() external onlyOwner { if (ownershipStatus != Status.RenouncementInProgress) { revert RenouncementNotInProgress(); } delete owner; delete ownershipStatus; emit NewOwner(address(0)); } /** * @notice This function is used to confirm the ownership transfer. * @dev This function can only be called by the current potential owner. */ function confirmOwnershipTransfer() external { if (ownershipStatus != Status.TransferInProgress) { revert TransferNotInProgress(); } if (msg.sender != potentialOwner) { revert WrongPotentialOwner(); } owner = msg.sender; delete ownershipStatus; delete potentialOwner; emit NewOwner(msg.sender); } /** * @notice This function is used to initiate the transfer of ownership to a new owner. * @param newPotentialOwner New potential owner address */ function initiateOwnershipTransfer(address newPotentialOwner) external onlyOwner { if (ownershipStatus != Status.NoOngoingTransfer) { revert TransferAlreadyInProgress(); } ownershipStatus = Status.TransferInProgress; potentialOwner = newPotentialOwner; /** * @dev This function can only be called by the owner, so msg.sender is the owner. * We don't have to SLOAD the owner again. */ emit InitiateOwnershipTransfer(msg.sender, newPotentialOwner); } /** * @notice This function is used to initiate the ownership renouncement. */ function initiateOwnershipRenouncement() external onlyOwner { if (ownershipStatus != Status.NoOngoingTransfer) { revert TransferAlreadyInProgress(); } ownershipStatus = Status.RenouncementInProgress; emit InitiateOwnershipRenouncement(); } function _onlyOwner() private view { if (msg.sender != owner) revert NotOwner(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /** * @title Pausable * @notice This contract makes it possible to pause the contract. * It is adjusted from OpenZeppelin. * @author LooksRare protocol team (👀,💎) */ abstract contract Pausable { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); error IsPaused(); error NotPaused(); bool private _paused; /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { _requireNotPaused(); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { _requirePaused(); _; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { if (paused()) { revert IsPaused(); } } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { if (!paused()) { revert NotPaused(); } } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(msg.sender); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(msg.sender); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; // Interfaces import {IReentrancyGuard} from "./interfaces/IReentrancyGuard.sol"; /** * @title ReentrancyGuard * @notice This contract protects against reentrancy attacks. * It is adjusted from OpenZeppelin. * @author LooksRare protocol team (👀,💎) */ abstract contract ReentrancyGuard is IReentrancyGuard { uint256 private _status; /** * @notice Modifier to wrap functions to prevent reentrancy calls. */ modifier nonReentrant() { if (_status == 2) { revert ReentrancyFail(); } _status = 2; _; _status = 1; } constructor() { _status = 1; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; enum TokenType { ERC20, ERC721, ERC1155 }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; // Enums import {TokenType} from "../enums/TokenType.sol"; /** * @title ITransferManager * @author LooksRare protocol team (👀,💎) */ interface ITransferManager { /** * @notice This struct is only used for transferBatchItemsAcrossCollections. * @param tokenAddress Token address * @param tokenType 0 for ERC721, 1 for ERC1155 * @param itemIds Array of item ids to transfer * @param amounts Array of amounts to transfer */ struct BatchTransferItem { address tokenAddress; TokenType tokenType; uint256[] itemIds; uint256[] amounts; } /** * @notice It is emitted if operators' approvals to transfer NFTs are granted by a user. * @param user Address of the user * @param operators Array of operator addresses */ event ApprovalsGranted(address user, address[] operators); /** * @notice It is emitted if operators' approvals to transfer NFTs are revoked by a user. * @param user Address of the user * @param operators Array of operator addresses */ event ApprovalsRemoved(address user, address[] operators); /** * @notice It is emitted if a new operator is added to the global allowlist. * @param operator Operator address */ event OperatorAllowed(address operator); /** * @notice It is emitted if an operator is removed from the global allowlist. * @param operator Operator address */ event OperatorRemoved(address operator); /** * @notice It is returned if the operator to approve has already been approved by the user. */ error OperatorAlreadyApprovedByUser(); /** * @notice It is returned if the operator to revoke has not been previously approved by the user. */ error OperatorNotApprovedByUser(); /** * @notice It is returned if the transfer caller is already allowed by the owner. * @dev This error can only be returned for owner operations. */ error OperatorAlreadyAllowed(); /** * @notice It is returned if the operator to approve is not in the global allowlist defined by the owner. * @dev This error can be returned if the user tries to grant approval to an operator address not in the * allowlist or if the owner tries to remove the operator from the global allowlist. */ error OperatorNotAllowed(); /** * @notice It is returned if the transfer caller is invalid. * For a transfer called to be valid, the operator must be in the global allowlist and * approved by the 'from' user. */ error TransferCallerInvalid(); /** * @notice This function transfers ERC20 tokens. * @param tokenAddress Token address * @param from Sender address * @param to Recipient address * @param amount amount */ function transferERC20( address tokenAddress, address from, address to, uint256 amount ) external; /** * @notice This function transfers a single item for a single ERC721 collection. * @param tokenAddress Token address * @param from Sender address * @param to Recipient address * @param itemId Item ID */ function transferItemERC721( address tokenAddress, address from, address to, uint256 itemId ) external; /** * @notice This function transfers items for a single ERC721 collection. * @param tokenAddress Token address * @param from Sender address * @param to Recipient address * @param itemIds Array of itemIds * @param amounts Array of amounts */ function transferItemsERC721( address tokenAddress, address from, address to, uint256[] calldata itemIds, uint256[] calldata amounts ) external; /** * @notice This function transfers a single item for a single ERC1155 collection. * @param tokenAddress Token address * @param from Sender address * @param to Recipient address * @param itemId Item ID * @param amount Amount */ function transferItemERC1155( address tokenAddress, address from, address to, uint256 itemId, uint256 amount ) external; /** * @notice This function transfers items for a single ERC1155 collection. * @param tokenAddress Token address * @param from Sender address * @param to Recipient address * @param itemIds Array of itemIds * @param amounts Array of amounts * @dev It does not allow batch transferring if from = msg.sender since native function should be used. */ function transferItemsERC1155( address tokenAddress, address from, address to, uint256[] calldata itemIds, uint256[] calldata amounts ) external; /** * @notice This function transfers items across an array of tokens that can be ERC20, ERC721 and ERC1155. * @param items Array of BatchTransferItem * @param from Sender address * @param to Recipient address */ function transferBatchItemsAcrossCollections( BatchTransferItem[] calldata items, address from, address to ) external; /** * @notice This function allows a user to grant approvals for an array of operators. * Users cannot grant approvals if the operator is not allowed by this contract's owner. * @param operators Array of operator addresses * @dev Each operator address must be globally allowed to be approved. */ function grantApprovals(address[] calldata operators) external; /** * @notice This function allows a user to revoke existing approvals for an array of operators. * @param operators Array of operator addresses * @dev Each operator address must be approved at the user level to be revoked. */ function revokeApprovals(address[] calldata operators) external; /** * @notice This function allows an operator to be added for the shared transfer system. * Once the operator is allowed, users can grant NFT approvals to this operator. * @param operator Operator address to allow * @dev Only callable by owner. */ function allowOperator(address operator) external; /** * @notice This function allows the user to remove an operator for the shared transfer system. * @param operator Operator address to remove * @dev Only callable by owner. */ function removeOperator(address operator) external; /** * @notice This returns whether the user has approved the operator address. * The first address is the user and the second address is the operator. */ function hasUserApprovedOperator(address user, address operator) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol) pragma solidity ^0.8.20; /** * @dev Standard ERC20 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens. */ interface IERC20Errors { /** * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. * @param balance Current balance for the interacting account. * @param needed Minimum amount required to perform a transfer. */ error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC20InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC20InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. * @param spender Address that may be allowed to operate on tokens without being their owner. * @param allowance Amount of tokens a `spender` is allowed to operate with. * @param needed Minimum amount required to perform a transfer. */ error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC20InvalidApprover(address approver); /** * @dev Indicates a failure with the `spender` to be approved. Used in approvals. * @param spender Address that may be allowed to operate on tokens without being their owner. */ error ERC20InvalidSpender(address spender); } /** * @dev Standard ERC721 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens. */ interface IERC721Errors { /** * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20. * Used in balance queries. * @param owner Address of the current owner of a token. */ error ERC721InvalidOwner(address owner); /** * @dev Indicates a `tokenId` whose `owner` is the zero address. * @param tokenId Identifier number of a token. */ error ERC721NonexistentToken(uint256 tokenId); /** * @dev Indicates an error related to the ownership over a particular token. Used in transfers. * @param sender Address whose tokens are being transferred. * @param tokenId Identifier number of a token. * @param owner Address of the current owner of a token. */ error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC721InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC721InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `operator`’s approval. Used in transfers. * @param operator Address that may be allowed to operate on tokens without being their owner. * @param tokenId Identifier number of a token. */ error ERC721InsufficientApproval(address operator, uint256 tokenId); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC721InvalidApprover(address approver); /** * @dev Indicates a failure with the `operator` to be approved. Used in approvals. * @param operator Address that may be allowed to operate on tokens without being their owner. */ error ERC721InvalidOperator(address operator); } /** * @dev Standard ERC1155 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens. */ interface IERC1155Errors { /** * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. * @param balance Current balance for the interacting account. * @param needed Minimum amount required to perform a transfer. * @param tokenId Identifier number of a token. */ error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC1155InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC1155InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `operator`’s approval. Used in transfers. * @param operator Address that may be allowed to operate on tokens without being their owner. * @param owner Address of the current owner of a token. */ error ERC1155MissingApprovalForAll(address operator, address owner); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC1155InvalidApprover(address approver); /** * @dev Indicates a failure with the `operator` to be approved. Used in approvals. * @param operator Address that may be allowed to operate on tokens without being their owner. */ error ERC1155InvalidOperator(address operator); /** * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation. * Used in batch transfers. * @param idsLength Length of the array of token identifiers * @param valuesLength Length of the array of token amounts */ error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol) pragma solidity ^0.8.20; import {IERC20} from "../token/ERC20/IERC20.sol"; import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol"; /** * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626]. */ interface IERC4626 is IERC20, IERC20Metadata { event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /** * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. * * - MUST be an ERC-20 token contract. * - MUST NOT revert. */ function asset() external view returns (address assetTokenAddress); /** * @dev Returns the total amount of the underlying asset that is “managed” by Vault. * * - SHOULD include any compounding that occurs from yield. * - MUST be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT revert. */ function totalAssets() external view returns (uint256 totalManagedAssets); /** * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToShares(uint256 assets) external view returns (uint256 shares); /** * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToAssets(uint256 shares) external view returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, * through a deposit call. * * - MUST return a limited value if receiver is subject to some deposit limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. * - MUST NOT revert. */ function maxDeposit(address receiver) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given * current on-chain conditions. * * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called * in the same transaction. * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the * deposit would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewDeposit(uint256 assets) external view returns (uint256 shares); /** * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * deposit execution, and are accounted for during deposit. * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function deposit(uint256 assets, address receiver) external returns (uint256 shares); /** * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. * - MUST return a limited value if receiver is subject to some mint limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. * - MUST NOT revert. */ function maxMint(address receiver) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given * current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the * same transaction. * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint * would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by minting. */ function previewMint(uint256 shares) external view returns (uint256 assets); /** * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint * execution, and are accounted for during mint. * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function mint(uint256 shares, address receiver) external returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the * Vault, through a withdraw call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST NOT revert. */ function maxWithdraw(address owner) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, * given current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if * called * in the same transaction. * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though * the withdrawal would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewWithdraw(uint256 assets) external view returns (uint256 shares); /** * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * withdraw execution, and are accounted for during withdraw. * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares); /** * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, * through a redeem call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. * - MUST NOT revert. */ function maxRedeem(address owner) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, * given current on-chain conditions. * * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the * same transaction. * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the * redemption would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by redeeming. */ function previewRedeem(uint256 shares) external view returns (uint256 assets); /** * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * redeem execution, and are accounted for during redeem. * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "./IERC20.sol"; import {IERC20Metadata} from "./extensions/IERC20Metadata.sol"; import {Context} from "../../utils/Context.sol"; import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * The default value of {decimals} is 18. To change this, you should override * this function so it returns a different value. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. */ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { mapping(address account => uint256) private _balances; mapping(address account => mapping(address spender => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the default value returned by this function, unless * it's overridden. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `value`. */ function transfer(address to, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _transfer(owner, to, value); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, value); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `value`. * - the caller must have allowance for ``from``'s tokens of at least * `value`. */ function transferFrom(address from, address to, uint256 value) public virtual returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, value); _transfer(from, to, value); return true; } /** * @dev Moves a `value` amount of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _transfer(address from, address to, uint256 value) internal { if (from == address(0)) { revert ERC20InvalidSender(address(0)); } if (to == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(from, to, value); } /** * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding * this function. * * Emits a {Transfer} event. */ function _update(address from, address to, uint256 value) internal virtual { if (from == address(0)) { // Overflow check required: The rest of the code assumes that totalSupply never overflows _totalSupply += value; } else { uint256 fromBalance = _balances[from]; if (fromBalance < value) { revert ERC20InsufficientBalance(from, fromBalance, value); } unchecked { // Overflow not possible: value <= fromBalance <= totalSupply. _balances[from] = fromBalance - value; } } if (to == address(0)) { unchecked { // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply. _totalSupply -= value; } } else { unchecked { // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256. _balances[to] += value; } } emit Transfer(from, to, value); } /** * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0). * Relies on the `_update` mechanism * * Emits a {Transfer} event with `from` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _mint(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(address(0), account, value); } /** * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply. * Relies on the `_update` mechanism. * * Emits a {Transfer} event with `to` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead */ function _burn(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidSender(address(0)); } _update(account, address(0), value); } /** * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. * * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. */ function _approve(address owner, address spender, uint256 value) internal { _approve(owner, spender, value, true); } /** * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event. * * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any * `Approval` event during `transferFrom` operations. * * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to * true using the following override: * ``` * function _approve(address owner, address spender, uint256 value, bool) internal virtual override { * super._approve(owner, spender, value, true); * } * ``` * * Requirements are the same as {_approve}. */ function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { if (owner == address(0)) { revert ERC20InvalidApprover(address(0)); } if (spender == address(0)) { revert ERC20InvalidSpender(address(0)); } _allowances[owner][spender] = value; if (emitEvent) { emit Approval(owner, spender, value); } } /** * @dev Updates `owner` s allowance for `spender` based on spent `value`. * * Does not update the allowance value in case of infinite allowance. * Revert if not enough allowance is available. * * Does not emit an {Approval} event. */ function _spendAllowance(address owner, address spender, uint256 value) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { if (currentAllowance < value) { revert ERC20InsufficientAllowance(spender, currentAllowance, value); } unchecked { _approve(owner, spender, currentAllowance - value, false); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC4626.sol) pragma solidity ^0.8.20; import {IERC20, IERC20Metadata, ERC20} from "../ERC20.sol"; import {SafeERC20} from "../utils/SafeERC20.sol"; import {IERC4626} from "../../../interfaces/IERC4626.sol"; import {Math} from "../../../utils/math/Math.sol"; /** * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626]. * * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this * contract and not the "assets" token which is an independent contract. * * [CAUTION] * ==== * In empty (or nearly empty) ERC-4626 vaults, deposits are at high risk of being stolen through frontrunning * with a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation * attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial * deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may * similarly be affected by slippage. Users can protect against this attack as well as unexpected slippage in general by * verifying the amount received is as expected, using a wrapper that performs these checks such as * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router]. * * Since v4.9, this implementation uses virtual assets and shares to mitigate that risk. The `_decimalsOffset()` * corresponds to an offset in the decimal representation between the underlying asset's decimals and the vault * decimals. This offset also determines the rate of virtual shares to virtual assets in the vault, which itself * determines the initial exchange rate. While not fully preventing the attack, analysis shows that the default offset * (0) makes it non-profitable, as a result of the value being captured by the virtual shares (out of the attacker's * donation) matching the attacker's expected gains. With a larger offset, the attack becomes orders of magnitude more * expensive than it is profitable. More details about the underlying math can be found * xref:erc4626.adoc#inflation-attack[here]. * * The drawback of this approach is that the virtual shares do capture (a very small) part of the value being accrued * to the vault. Also, if the vault experiences losses, the users try to exit the vault, the virtual shares and assets * will cause the first user to exit to experience reduced losses in detriment to the last users that will experience * bigger losses. Developers willing to revert back to the pre-v4.9 behavior just need to override the * `_convertToShares` and `_convertToAssets` functions. * * To learn more, check out our xref:ROOT:erc4626.adoc[ERC-4626 guide]. * ==== */ abstract contract ERC4626 is ERC20, IERC4626 { using Math for uint256; IERC20 private immutable _asset; uint8 private immutable _underlyingDecimals; /** * @dev Attempted to deposit more assets than the max amount for `receiver`. */ error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max); /** * @dev Attempted to mint more shares than the max amount for `receiver`. */ error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max); /** * @dev Attempted to withdraw more assets than the max amount for `receiver`. */ error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max); /** * @dev Attempted to redeem more shares than the max amount for `receiver`. */ error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max); /** * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777). */ constructor(IERC20 asset_) { (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_); _underlyingDecimals = success ? assetDecimals : 18; _asset = asset_; } /** * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way. */ function _tryGetAssetDecimals(IERC20 asset_) private view returns (bool, uint8) { (bool success, bytes memory encodedDecimals) = address(asset_).staticcall( abi.encodeCall(IERC20Metadata.decimals, ()) ); if (success && encodedDecimals.length >= 32) { uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256)); if (returnedDecimals <= type(uint8).max) { return (true, uint8(returnedDecimals)); } } return (false, 0); } /** * @dev Decimals are computed by adding the decimal offset on top of the underlying asset's decimals. This * "original" value is cached during construction of the vault contract. If this read operation fails (e.g., the * asset has not been created yet), a default of 18 is used to represent the underlying asset's decimals. * * See {IERC20Metadata-decimals}. */ function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) { return _underlyingDecimals + _decimalsOffset(); } /** @dev See {IERC4626-asset}. */ function asset() public view virtual returns (address) { return address(_asset); } /** @dev See {IERC4626-totalAssets}. */ function totalAssets() public view virtual returns (uint256) { return _asset.balanceOf(address(this)); } /** @dev See {IERC4626-convertToShares}. */ function convertToShares(uint256 assets) public view virtual returns (uint256) { return _convertToShares(assets, Math.Rounding.Floor); } /** @dev See {IERC4626-convertToAssets}. */ function convertToAssets(uint256 shares) public view virtual returns (uint256) { return _convertToAssets(shares, Math.Rounding.Floor); } /** @dev See {IERC4626-maxDeposit}. */ function maxDeposit(address) public view virtual returns (uint256) { return type(uint256).max; } /** @dev See {IERC4626-maxMint}. */ function maxMint(address) public view virtual returns (uint256) { return type(uint256).max; } /** @dev See {IERC4626-maxWithdraw}. */ function maxWithdraw(address owner) public view virtual returns (uint256) { return _convertToAssets(balanceOf(owner), Math.Rounding.Floor); } /** @dev See {IERC4626-maxRedeem}. */ function maxRedeem(address owner) public view virtual returns (uint256) { return balanceOf(owner); } /** @dev See {IERC4626-previewDeposit}. */ function previewDeposit(uint256 assets) public view virtual returns (uint256) { return _convertToShares(assets, Math.Rounding.Floor); } /** @dev See {IERC4626-previewMint}. */ function previewMint(uint256 shares) public view virtual returns (uint256) { return _convertToAssets(shares, Math.Rounding.Ceil); } /** @dev See {IERC4626-previewWithdraw}. */ function previewWithdraw(uint256 assets) public view virtual returns (uint256) { return _convertToShares(assets, Math.Rounding.Ceil); } /** @dev See {IERC4626-previewRedeem}. */ function previewRedeem(uint256 shares) public view virtual returns (uint256) { return _convertToAssets(shares, Math.Rounding.Floor); } /** @dev See {IERC4626-deposit}. */ function deposit(uint256 assets, address receiver) public virtual returns (uint256) { uint256 maxAssets = maxDeposit(receiver); if (assets > maxAssets) { revert ERC4626ExceededMaxDeposit(receiver, assets, maxAssets); } uint256 shares = previewDeposit(assets); _deposit(_msgSender(), receiver, assets, shares); return shares; } /** @dev See {IERC4626-mint}. * * As opposed to {deposit}, minting is allowed even if the vault is in a state where the price of a share is zero. * In this case, the shares will be minted without requiring any assets to be deposited. */ function mint(uint256 shares, address receiver) public virtual returns (uint256) { uint256 maxShares = maxMint(receiver); if (shares > maxShares) { revert ERC4626ExceededMaxMint(receiver, shares, maxShares); } uint256 assets = previewMint(shares); _deposit(_msgSender(), receiver, assets, shares); return assets; } /** @dev See {IERC4626-withdraw}. */ function withdraw(uint256 assets, address receiver, address owner) public virtual returns (uint256) { uint256 maxAssets = maxWithdraw(owner); if (assets > maxAssets) { revert ERC4626ExceededMaxWithdraw(owner, assets, maxAssets); } uint256 shares = previewWithdraw(assets); _withdraw(_msgSender(), receiver, owner, assets, shares); return shares; } /** @dev See {IERC4626-redeem}. */ function redeem(uint256 shares, address receiver, address owner) public virtual returns (uint256) { uint256 maxShares = maxRedeem(owner); if (shares > maxShares) { revert ERC4626ExceededMaxRedeem(owner, shares, maxShares); } uint256 assets = previewRedeem(shares); _withdraw(_msgSender(), receiver, owner, assets, shares); return assets; } /** * @dev Internal conversion function (from assets to shares) with support for rounding direction. */ function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) { return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding); } /** * @dev Internal conversion function (from shares to assets) with support for rounding direction. */ function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) { return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding); } /** * @dev Deposit/mint common workflow. */ function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual { // If _asset is ERC777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer, // calls the vault, which is assumed not malicious. // // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the // assets are transferred and before the shares are minted, which is a valid state. // slither-disable-next-line reentrancy-no-eth SafeERC20.safeTransferFrom(_asset, caller, address(this), assets); _mint(receiver, shares); emit Deposit(caller, receiver, assets, shares); } /** * @dev Withdraw/redeem common workflow. */ function _withdraw( address caller, address receiver, address owner, uint256 assets, uint256 shares ) internal virtual { if (caller != owner) { _spendAllowance(owner, caller, shares); } // If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer, // calls the vault, which is assumed not malicious. // // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the // shares are burned and after the assets are transferred, which is a valid state. _burn(owner, shares); SafeERC20.safeTransfer(_asset, receiver, assets); emit Withdraw(caller, receiver, owner, assets, shares); } function _decimalsOffset() internal view virtual returns (uint8) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @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. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ 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]. * * CAUTION: See Security Considerations above. */ 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 v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @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 value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` 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 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../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 An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @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.encodeCall(token.transfer, (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.encodeCall(token.transferFrom, (from, to, 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); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @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.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @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); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @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(token).code.length > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @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 or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * 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. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @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`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) 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 FailedInnerCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; /** * @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; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol) pragma solidity ^0.8.20; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Muldiv operation overflow. */ error MathOverflowedMulDiv(); enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the addition of two unsigned integers, with an overflow flag. */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @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 towards infinity instead * of rounding towards zero. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { if (b == 0) { // Guarantee the same behavior as in a regular Solidity division. return a / b; } // (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 = x * y; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) 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. if (denominator <= prod1) { revert MathOverflowedMulDiv(); } /////////////////////////////////////////////// // 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. uint256 twos = denominator & (0 - denominator); 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 (unsignedRoundsUp(rounding) && 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 * towards zero. * * 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 + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * 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 + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10 of a positive value rounded towards zero. * 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 + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256 of a positive value rounded towards zero. * 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 + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0); } } /** * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. */ function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { return uint8(rounding) % 2 == 1; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; enum YieldMode { AUTOMATIC, VOID, CLAIMABLE } enum GasMode { VOID, CLAIMABLE } interface IBlast { // configure function configureContract(address contractAddress, YieldMode _yield, GasMode gasMode, address governor) external; function configure(YieldMode _yield, GasMode gasMode, address governor) external; // base configuration options function configureClaimableYield() external; function configureClaimableYieldOnBehalf(address contractAddress) external; function configureAutomaticYield() external; function configureAutomaticYieldOnBehalf(address contractAddress) external; function configureVoidYield() external; function configureVoidYieldOnBehalf(address contractAddress) external; function configureClaimableGas() external; function configureClaimableGasOnBehalf(address contractAddress) external; function configureVoidGas() external; function configureVoidGasOnBehalf(address contractAddress) external; function configureGovernor(address _governor) external; function configureGovernorOnBehalf(address _newGovernor, address contractAddress) external; // claim yield function claimYield(address contractAddress, address recipientOfYield, uint256 amount) external returns (uint256); function claimAllYield(address contractAddress, address recipientOfYield) external returns (uint256); // claim gas function claimAllGas(address contractAddress, address recipientOfGas) external returns (uint256); function claimGasAtMinClaimRate( address contractAddress, address recipientOfGas, uint256 minClaimRateBips ) external returns (uint256); function claimMaxGas(address contractAddress, address recipientOfGas) external returns (uint256); function claimGas( address contractAddress, address recipientOfGas, uint256 gasToClaim, uint256 gasSecondsToConsume ) external returns (uint256); // read functions function readClaimableYield(address contractAddress) external view returns (uint256); function readYieldConfiguration(address contractAddress) external view returns (uint8); function readGasParams( address contractAddress ) external view returns (uint256 etherSeconds, uint256 etherBalance, uint256 lastUpdated, GasMode); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; interface IBlastPoints { function configurePointsOperator(address operator) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; enum YieldMode { AUTOMATIC, VOID, CLAIMABLE } interface IERC20Rebasing { // changes the yield mode of the caller and update the balance // to reflect the configuration function configure(YieldMode) external returns (uint256); // "claimable" yield mode accounts can call this this claim their yield // to another address function claim(address recipient, uint256 amount) external returns (uint256); // read the claimable amount for an account function getClaimableAmount(address account) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; interface ILiquidityPool { event PayoutTransferred(address game, address receiver, address currency, uint256 amount); event InsufficientFundsForPayout(address game, address receiver, address currency, uint256 shortfall); event ProtocolFeeTransferred(address game, address protocolFeeRecipient, address currency, uint256 amount); error LiquidityPool__NotAuthorized(); error LiquidityPool__UnsupportedOperation(); /** * @notice Returns the liquidity pool router contract address */ function LIQUIDITY_POOL_ROUTER() external view returns (address); /** * @notice Transfer payout to the player, only the game configuration manager can call this function * @param game Game contract address * @param amount Amount to transfer * @param receiver Prize receiver */ function transferPayoutToPlayer(address game, uint256 amount, address receiver) external; /** * @notice Transfer protocol fee, only the game configuration manager can call this function * @param game Game contract address * @param amount Amount to transfer * @param protocolFeeRecipient Protocol fee recipient */ function transferProtocolFee(address game, uint256 amount, address protocolFeeRecipient) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; import {OwnableTwoSteps} from "@looksrare/contracts-libs/contracts/OwnableTwoSteps.sol"; import {ReentrancyGuard} from "@looksrare/contracts-libs/contracts/ReentrancyGuard.sol"; import {Pausable} from "@looksrare/contracts-libs/contracts/Pausable.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {ERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; import {ILiquidityPool} from "./interfaces/ILiquidityPool.sol"; import {IBlast, YieldMode, GasMode} from "./interfaces/IBlast.sol"; import {IBlastPoints} from "./interfaces/IBlastPoints.sol"; import {IERC20Rebasing} from "./interfaces/IERC20Rebasing.sol"; import {LowLevelERC20Transfer} from "@looksrare/contracts-libs/contracts/lowLevelCallers/LowLevelERC20Transfer.sol"; /** * @title LiquidityPool * @notice This contract allows depositors to act as the counterparty of players of our games. * @author YOLO Games protocol team */ abstract contract LiquidityPool is ERC4626, OwnableTwoSteps, ReentrancyGuard, Pausable, ILiquidityPool, LowLevelERC20Transfer { /** * @notice Liquidity manager contract address */ address public immutable GAME_CONFIGURATION_MANAGER; /** * @notice Liquidity pool router contract address */ address public immutable LIQUIDITY_POOL_ROUTER; /** * @param _name Vault share name * @param _symbol Vault share symbol * @param _owner Owner of the contract * @param _asset Vault asset contract address * @param _gameConfigurationManager Liquidity manager contract address * @param _liquidityPoolRouter Liquidity pool router contract address * @param _blast Blast precompile * @param _blastPoints Blast points * @param _blastPointsOperator Blast points operator */ constructor( string memory _name, string memory _symbol, address _owner, address _asset, address _gameConfigurationManager, address _liquidityPoolRouter, address _blast, address _blastPoints, address _blastPointsOperator ) ERC20(_name, _symbol) ERC4626(IERC20(_asset)) OwnableTwoSteps(_owner) { GAME_CONFIGURATION_MANAGER = _gameConfigurationManager; LIQUIDITY_POOL_ROUTER = _liquidityPoolRouter; IBlast(_blast).configure(YieldMode.CLAIMABLE, GasMode.CLAIMABLE, _owner); IBlastPoints(_blastPoints).configurePointsOperator(_blastPointsOperator); } /** * @inheritdoc ERC4626 */ function deposit(uint256 assets, address receiver) public override returns (uint256) { _onlyLiquidityPoolRouter(); return super.deposit(assets, receiver); } /** * @inheritdoc ERC4626 */ function redeem(uint256 shares, address receiver, address owner) public override returns (uint256) { _onlyLiquidityPoolRouter(); return super.redeem(shares, receiver, owner); } /** * @notice Mint is not supported by this contract */ function mint(uint256, address) public pure override returns (uint256) { revert LiquidityPool__UnsupportedOperation(); } /** * @notice Withdraw is not supported by this contract */ function withdraw(uint256, address, address) public pure override returns (uint256) { revert LiquidityPool__UnsupportedOperation(); } /** * @notice Toggle paused state. Only callable by contract owner. */ function togglePaused() external onlyOwner { paused() ? _unpause() : _pause(); } /** * @notice Claim Blast yield. Only callable by contract owner. * @param receiver Receiver of the yield */ function claimYield(address receiver) external onlyOwner { IERC20Rebasing rebasingAsset = IERC20Rebasing(asset()); uint256 claimableAmount = rebasingAsset.getClaimableAmount(address(this)); if (claimableAmount != 0) { rebasingAsset.claim(receiver, claimableAmount); } } function _decimalsOffset() internal pure override returns (uint8) { return 6; } function _onlyGameConfigurationManager() internal view { if (msg.sender != GAME_CONFIGURATION_MANAGER) { revert LiquidityPool__NotAuthorized(); } } function _onlyLiquidityPoolRouter() internal view { if (msg.sender != LIQUIDITY_POOL_ROUTER) { revert LiquidityPool__NotAuthorized(); } } }
{ "viaIR": true, "optimizer": { "enabled": true, "runs": 888888 }, "evmVersion": "paris", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_weth","type":"address"},{"internalType":"address","name":"_usdb","type":"address"},{"internalType":"address","name":"_transferManager","type":"address"},{"internalType":"address","name":"_blast","type":"address"},{"internalType":"address","name":"_blastPoints","type":"address"},{"internalType":"address","name":"_blastPointsOperator","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ERC20TransferFail","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"IsPaused","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__DepositAmountTooHigh","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__DepositAmountTooLow","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__FinalizationForAllDelayTooHigh","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__FinalizationForAllDelayTooLow","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__FinalizationForAllIsNotOpen","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__FinalizationIncentiveNotPaid","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__FinalizationIncentiveTooHigh","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__InvalidTimelockDelay","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__MaxDepositAmountTooHigh","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__MinDepositAmountTooHigh","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__NoLiquidityPoolForToken","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__NoOngoingDeposit","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__NoOngoingRedemption","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__OngoingDeposit","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__OngoingRedemption","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__TimelockIsNotOver","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__TokenAlreadyHasLiquidityPool","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__WETHDepositNotAllowed","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__ZeroExpectedAssets","type":"error"},{"inputs":[],"name":"LiquidityPoolRouter__ZeroExpectedShares","type":"error"},{"inputs":[],"name":"NoOngoingTransferInProgress","type":"error"},{"inputs":[],"name":"NotAContract","type":"error"},{"inputs":[],"name":"NotOwner","type":"error"},{"inputs":[],"name":"NotPaused","type":"error"},{"inputs":[],"name":"ReentrancyFail","type":"error"},{"inputs":[],"name":"RenouncementNotInProgress","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"TransferAlreadyInProgress","type":"error"},{"inputs":[],"name":"TransferNotInProgress","type":"error"},{"inputs":[],"name":"WrongPotentialOwner","type":"error"},{"anonymous":false,"inputs":[],"name":"CancelOwnershipTransfer","type":"event"},{"anonymous":false,"inputs":[],"name":"InitiateOwnershipRenouncement","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":false,"internalType":"address","name":"potentialOwner","type":"address"}],"name":"InitiateOwnershipTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"liquidityPool","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sharesMinted","type":"uint256"}],"name":"LiquidityPoolRouter__DepositFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"liquidityPool","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expectedShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"finalizationIncentive","type":"uint256"}],"name":"LiquidityPoolRouter__DepositInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidityPool","type":"address"},{"indexed":false,"internalType":"uint256","name":"minDepositAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxDepositAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxBalance","type":"uint256"}],"name":"LiquidityPoolRouter__DepositLimitUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timelockDelay","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"finalizationForAllDelay","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"finalizationIncentive","type":"uint256"}],"name":"LiquidityPoolRouter__FinalizationParamsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"liquidityPool","type":"address"}],"name":"LiquidityPoolRouter__LiquidityPoolAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"liquidityPool","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assetsRedeemed","type":"uint256"}],"name":"LiquidityPoolRouter__RedemptionFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"liquidityPool","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expectedAssets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"finalizationIncentive","type":"uint256"}],"name":"LiquidityPoolRouter__RedemptionInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"DEPOSIT_FEE_BASIS_POINTS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRANSFER_MANAGER","outputs":[{"internalType":"contract ITransferManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDB","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"liquidityPool","type":"address"}],"name":"addLiquidityPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelOwnershipTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"claimYield","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"confirmOwnershipRenouncement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"confirmOwnershipTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"liquidityPool","type":"address"}],"name":"depositLimit","outputs":[{"internalType":"uint256","name":"minDepositAmount","type":"uint256"},{"internalType":"uint256","name":"maxDepositAmount","type":"uint256"},{"internalType":"uint256","name":"maxBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"deposits","outputs":[{"internalType":"address","name":"liquidityPool","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expectedShares","type":"uint256"},{"internalType":"uint256","name":"initializedAt","type":"uint256"},{"internalType":"uint256","name":"finalizationIncentive","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"finalizationParams","outputs":[{"internalType":"uint80","name":"timelockDelay","type":"uint80"},{"internalType":"uint80","name":"finalizationForAllDelay","type":"uint80"},{"internalType":"uint80","name":"finalizationIncentive","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"depositor","type":"address"}],"name":"finalizeDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"redeemer","type":"address"}],"name":"finalizeRedemption","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initiateOwnershipRenouncement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPotentialOwner","type":"address"}],"name":"initiateOwnershipTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"liquidityPools","outputs":[{"internalType":"address","name":"liquidityPool","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownershipStatus","outputs":[{"internalType":"enum IOwnableTwoSteps.Status","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"liquidityPool","type":"address"}],"name":"pendingDeposits","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"liquidityPool","type":"address"}],"name":"pendingWithdrawals","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"potentialOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"redemptions","outputs":[{"internalType":"address","name":"liquidityPool","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"expectedAssets","type":"uint256"},{"internalType":"uint256","name":"initializedAt","type":"uint256"},{"internalType":"uint256","name":"finalizationIncentive","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"liquidityPool","type":"address"},{"internalType":"uint256","name":"minDepositAmount","type":"uint256"},{"internalType":"uint256","name":"maxDepositAmount","type":"uint256"},{"internalType":"uint256","name":"maxBalance","type":"uint256"}],"name":"setDepositLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint80","name":"_timelockDelay","type":"uint80"},{"internalType":"uint80","name":"_finalizationForAllDelay","type":"uint80"},{"internalType":"uint80","name":"_finalizationIncentive","type":"uint80"}],"name":"setFinalizationParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"togglePaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60e0604090808252346200032457600060e08262003ddb8038038091620000278285620003c8565b833981010312620002e6576200003d82620003ec565b6020926200004d848201620003ec565b936200005b868301620003ec565b6200006960608401620003ec565b956200007860808501620003ec565b966200009560c06200008d60a08801620003ec565b9601620003ec565b9660018060a01b0380809816928360018060a01b03198454161783557f3edd90e7770f06fafde38004653b33870066c33bfc923ff6102acd601f85dfbc878d51868152a16001600255608085905260a08690521660c0528951909860608201916001600160401b038311818410176200039e5760607f0218c65ca00eb675f88ded3d89b4ff402d73ea8be15c060a7e8a0b0f1b3506f3918a948e52600a81528d8061012c92838c820152660110d9316ec00092839101527a0110d9316ec0000000000000000000012c0000000000000000000a61ffff60f01b600a541617600a55805192600a84528b840152820152a11690813b156200032457600091606483928c51948593849263c8992e6160e01b8452600260048501526001602485015260448401525af1801562000393576200037c575b50875183816024818b8a631a33757d60e01b9788845260026004850152165af1801562000372579186939185936200033b575b50602490898b519586948593845260026004850152165af1801562000331579084939291620002f7575b50501691823b15620002f357906024849283875195869485936336b91f2b60e01b85521660048401525af18015620002e957620002ce575b82516139d99081620004028239608051818181610b23015281816111f10152818161145401528181611a1d01528181611fb10152818161237d0152818161328201526134b3015260a0518181816108670152611fd7015260c05181818161033501528181610bbb01526129640152f35b620002da8291620003b4565b620002e657806200025e565b80fd5b83513d84823e3d90fd5b8380fd5b90809293503d831162000329575b620003118183620003c8565b8101031262000324578190388062000226565b600080fd5b503d62000305565b87513d88823e3d90fd5b9092809294503d83116200036a575b620003568183620003c8565b8101031262000324578491839138620001fc565b503d6200034a565b89513d8a823e3d90fd5b62000389919750620003b4565b60009538620001c9565b89513d6000823e3d90fd5b634e487b7160e01b600052604160045260246000fd5b6001600160401b0381116200039e57604052565b601f909101601f19168101906001600160401b038211908210176200039e57604052565b51906001600160a01b0382168203620003245756fe6080604052600436101561001b575b361561001957600080fd5b005b60003560e01c80630b0fd47e146101fb5780631e9a6950146101f657806323452b9c146101f15780632bb5a9e6146101ec57806331a0edec146101e757806336566f06146101e25780633e567539146101dd57806347e7ef24146101d85780635002b2ba146101d35780635358fbda146101ce57806353d790b6146101c95780635b6ac011146101c45780635c975abb146101bf5780637200b829146101ba5780637762df25146101b55780638da5cb5b146101b0578063999927df146101ab5780639ab4ffcf146101a65780639bc3f269146101a1578063ad5c46481461019c578063c0b6f56114610197578063c64fe82814610192578063e22e8e011461018d578063e97c2f5d14610188578063eb3349b914610183578063ee6a934c1461017e578063f01f7ce414610179578063f3f4370314610174578063fc7e286d1461016f5763ff98c1e70361000e57612a92565b6129f1565b612988565b612919565b612742565b6126d9565b61269f565b612533565b6124d5565b6123a1565b612332565b612291565b612046565b611f65565b611f13565b611ec1565b611d51565b611d10565b611c07565b6117aa565b61140e565b610f2d565b610ab8565b6109b3565b61088b565b61081c565b6107d1565b610656565b61028e565b610223565b73ffffffffffffffffffffffffffffffffffffffff81160361021e57565b600080fd5b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57602060043561026081610200565b73ffffffffffffffffffffffffffffffffffffffff8091166000526004825260406000205416604051908152f35b6040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5760048035906102c782610200565b602435916002805414610623576102e19060028055612d04565b906102ea612d5e565b60016103163373ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b01546105fc5773ffffffffffffffffffffffffffffffffffffffff90817f000000000000000000000000000000000000000000000000000000000000000016803b1561021e5785517fda3e8ce400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851683820190815233602082015230604082015260608101879052909160009183919082908490829060800103925af180156105de576105e3575b506020855180937f4cdad506000000000000000000000000000000000000000000000000000000008252818061040e898783019190602083019252565b039187165afa9182156105de576000926105ad575b50811561058657507f2e35b251a7f84ecdc4395881eedc1d3ae2e9f05ad4309b3e7d770537757b152f93610579916104e061046d600a5469ffffffffffffffffffff9060a01c1690565b69ffffffffffffffffffff610480612bd2565b73ffffffffffffffffffffffffffffffffffffffff881681529188602084015284868401524260608401521660808201526104db3373ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b612c01565b61050a8473ffffffffffffffffffffffffffffffffffffffff166000526009602052604060002090565b610515828254612cb7565b9055600a5460a01c69ffffffffffffffffffff165b915194859433869360809369ffffffffffffffffffff9397969260a087019873ffffffffffffffffffffffffffffffffffffffff80921688521660208701526040860152606085015216910152565b0390a16100196001600255565b84517f1c2510c7000000000000000000000000000000000000000000000000000000008152fd5b6105d091925060203d6020116105d7575b6105c88183612b76565b810190612bc3565b9038610423565b503d6105be565b612bb7565b806105f06105f692612b41565b8061064b565b386103d1565b83517ff69b65f6000000000000000000000000000000000000000000000000000000008152fd5b5082517f1bbee726000000000000000000000000000000000000000000000000000000008152fd5b600091031261021e57565b3461021e576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126107955761068e612da2565b60ff60015460a01c16600381101561076857801561073e57806106b2600192610798565b1461070c575b6106e57fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff60015416600155565b7f8eca980489e87f7dba4f26917aa4bfc906eb3f2b4f7b4b9fd0ff2b8bb3e21ae38180a180f35b6107397fffffffffffffffffffffffff000000000000000000000000000000000000000060015416600155565b6106b8565b60046040517fccf69db7000000000000000000000000000000000000000000000000000000008152fd5b6024827f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b80fd5b600311156107a257565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5760ff60015460a01c1660405160038210156107a2576020918152f35b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e576108c2612da2565b60035460ff811615610956575060035460ff81161561092c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166003557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60046040517f6cd60201000000000000000000000000000000000000000000000000000000008152fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00600191610982612ded565b16176003557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b3461021e576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610795576109eb612da2565b60ff60015460a01c16600381101561076857600203610a8e57610a317fffffffffffffffffffffffff000000000000000000000000000000000000000060005416600055565b610a5e7fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff60015416600155565b604051600081527f3edd90e7770f06fafde38004653b33870066c33bfc923ff6102acd601f85dfbc90602090a180f35b60046040517f045c5122000000000000000000000000000000000000000000000000000000008152fd5b6040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e576004803590610af182610200565b602435906002805414610f065760028055610b0a612ded565b73ffffffffffffffffffffffffffffffffffffffff91827f00000000000000000000000000000000000000000000000000000000000000001683851614610ede57610b5484612d04565b91610b5d612d5e565b610b7b610b74610b6c84612cc9565b612710900490565b8093612cdf565b90610b868285612e23565b6001610bb23373ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b0154610eb757847f00000000000000000000000000000000000000000000000000000000000000001695863b1561021e5787517fda3e8ce40000000000000000000000000000000000000000000000000000000080825273ffffffffffffffffffffffffffffffffffffffff83168483019081523360208201523060408201526060810186905260009991939192908a9082908190608001038183875af180156105de57610ea4575b50885473ffffffffffffffffffffffffffffffffffffffff1692823b15610ea0578a5190815273ffffffffffffffffffffffffffffffffffffffff918216858201908152336020820152919093166040820152606081018690528891839182908490829060800103925af180156105de57610e8d575b506020875180967fef8b30f70000000000000000000000000000000000000000000000000000000082528180610d0e878783019190602083019252565b039188165afa9485156105de578695610e6c575b508415610e45575094610e1b610e35927faeeb0857c3a75fe5aad3955b0147e40f58826a77f6f2bd632791e0c387f0b3e49697610ddf610d71600a5469ffffffffffffffffffff9060a01c1690565b69ffffffffffffffffffff610d84612bd2565b73ffffffffffffffffffffffffffffffffffffffff8a168152918460208401528a888401524260608401521660808201526104db3373ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b610e098673ffffffffffffffffffffffffffffffffffffffff166000526008602052604060002090565b610e14828254612cb7565b9055612cb7565b9361052a600a5469ffffffffffffffffffff9060a01c1690565b0390a1610e426001600255565b80f35b86517f2da2a1e1000000000000000000000000000000000000000000000000000000008152fd5b610e8691955060203d6020116105d7576105c88183612b76565b9338610d22565b806105f0610e9a92612b41565b38610cd1565b8980fd5b806105f0610eb192612b41565b38610c5b565b86517f3434dd01000000000000000000000000000000000000000000000000000000008152fd5b5083517f16c6503f000000000000000000000000000000000000000000000000000000008152fd5b83517f1bbee726000000000000000000000000000000000000000000000000000000008152fd5b3461021e576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e576004908135610f6b81610200565b60028054146113e557600280556001610fa48273ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b015480156113bc57610fec6003610fdb8473ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b0154610fe68161304d565b83613097565b61104f61103661101c8473ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b6040517f38d52e0f000000000000000000000000000000000000000000000000000000008152909373ffffffffffffffffffffffffffffffffffffffff851681838881845afa9283156105de5760009361138d575b5060026110d18673ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b0154876110fe8773ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b01549761115561110c612bd2565b600081526000868201526000604082015260006060820152600060808201526104db8973ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b61117f8873ffffffffffffffffffffffffffffffffffffffff166000526009602052604060002090565b61118a838254612cdf565b9055604080517fba0876520000000000000000000000000000000000000000000000000000000081528281018881523060208201819052928101929092529085908290819060600103816000885af19081156105de57600091611370575b50611215819a337f0000000000000000000000000000000000000000000000000000000000000000613132565b808310611299575050505050936105799161125282857fbe7bc3899b370584fa4121ff891418505639d5991dee9eba19287e4793a6cf979861327e565b604051948594338691608093969594919660a084019773ffffffffffffffffffffffffffffffffffffffff9283809216865216602085015216604083015260608201520152565b90929594919398506112ac84888461327e565b8860405180947f18160ddd00000000000000000000000000000000000000000000000000000000825281895afa9586156105de578461057997611343957fbe7bc3899b370584fa4121ff891418505639d5991dee9eba19287e4793a6cf979c600092611353575b50506113495760005461133d925073ffffffffffffffffffffffffffffffffffffffff1692612cdf565b91613395565b90611252565b61133d9192612cdf565b6113699250803d106105d7576105c88183612b76565b3880611313565b6113879150853d87116105d7576105c88183612b76565b386111e8565b6113ae919350823d84116113b5575b6113a68183612b76565b810190612cec565b91386110a4565b503d61139c565b836040517f299597ac000000000000000000000000000000000000000000000000000000008152fd5b826040517f1bbee726000000000000000000000000000000000000000000000000000000008152fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57600480359060028054146117825760028055611452612ded565b7f000000000000000000000000000000000000000000000000000000000000000061147c81612d04565b906114b16114ab61149c600a5469ffffffffffffffffffff9060a01c1690565b69ffffffffffffffffffff1690565b85612cb7565b3403611759576114cd6114c6610b6c86612cc9565b8095612cdf565b906114d88284612e23565b60016115043373ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b015461173057611535908561152e60005473ffffffffffffffffffffffffffffffffffffffff1690565b5a92613269565b604051927fef8b30f700000000000000000000000000000000000000000000000000000000845260208480611571858583019190602083019252565b038173ffffffffffffffffffffffffffffffffffffffff87165afa9384156105de5760009461170f575b5083156116e757506116817faeeb0857c3a75fe5aad3955b0147e40f58826a77f6f2bd632791e0c387f0b3e494610579926116576115e8600a5469ffffffffffffffffffff9060a01c1690565b69ffffffffffffffffffff6115fb612bd2565b73ffffffffffffffffffffffffffffffffffffffff89168152918460208401528960408401524260608401521660808201526104db3373ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b610e098573ffffffffffffffffffffffffffffffffffffffff166000526008602052604060002090565b9261169b600a5469ffffffffffffffffffff9060a01c1690565b6040805133815273ffffffffffffffffffffffffffffffffffffffff9095166020860152840194909452606083015269ffffffffffffffffffff909216608082015290819060a0820190565b6040517f2da2a1e1000000000000000000000000000000000000000000000000000000008152fd5b61172991945060203d6020116105d7576105c88183612b76565b923861159b565b836040517f3434dd01000000000000000000000000000000000000000000000000000000008152fd5b826040517f68b097d0000000000000000000000000000000000000000000000000000000008152fd5b6040517f1bbee726000000000000000000000000000000000000000000000000000000008152fd5b3461021e576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5760049081356117e881610200565b60028054146113e5576002805560016118218273ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b01548015611bde576118586003610fdb8473ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b61188861103661101c8473ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b9273ffffffffffffffffffffffffffffffffffffffff9081851691604051967f38d52e0f00000000000000000000000000000000000000000000000000000000885282888281875afa9788156105de57600098611bbf575b50600261190d8773ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b0154604051917fef8b30f700000000000000000000000000000000000000000000000000000000835284838061194a8a8583019190602083019252565b0381895afa9283156105de57600093611ba0575b508061198a8973ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b0154926119e1611998612bd2565b600081526000888201526000604082015260006060820152600060808201526104db8b73ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b611a0b8a73ffffffffffffffffffffffffffffffffffffffff166000526008602052604060002090565b611a16898254612cdf565b9055611a447f0000000000000000000000000000000000000000000000000000000000000000943386613132565b8210611ac657505050505093611a8183837f7eed4a581ebd679621ba6cad2ad0516cd66821e5853dd38d200f5396931ae380976105799594613496565b6040805133815273ffffffffffffffffffffffffffffffffffffffff9586166020820152959094169385019390935260608401526080830191909152819060a0820190565b6040517fb3d7f6b900000000000000000000000000000000000000000000000000000000815290810191825298939592949086908a9081906020010381845afa9283156105de577f7eed4a581ebd679621ba6cad2ad0516cd66821e5853dd38d200f5396931ae3809961057997600095611b81575b50508394611b4b89868584613496565b9681811690851603611b725750611b6d93611b6591612cdf565b905a92613269565b611252565b9250611b6d9361133d91612cdf565b611b98929550803d106105d7576105c88183612b76565b923880611b3b565b611bb8919350853d87116105d7576105c88183612b76565b913861195e565b611bd7919850833d85116113b5576113a68183612b76565b96386118e0565b836040517f3d89895d000000000000000000000000000000000000000000000000000000008152fd5b3461021e576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261079557611c3f612da2565b60015460ff8160a01c166003811015611ce357611cb9577fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674020000000000000000000000000000000000000000176001557f3ff05a45e46337fa1cbf20996d2eeb927280bce099f37252bcca1040609604ec8180a180f35b60046040517f74ed79ae000000000000000000000000000000000000000000000000000000008152fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57602060ff600354166040519015158152f35b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57600154600160ff8260a01c16611d9581610798565b03611e975773ffffffffffffffffffffffffffffffffffffffff163303611e6d57600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055611e0d7fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff60015416600155565b611e3a7fffffffffffffffffffffffff000000000000000000000000000000000000000060015416600155565b6040513381527f3edd90e7770f06fafde38004653b33870066c33bfc923ff6102acd601f85dfbc9080602081015b0390a1005b60046040517fafdcfb92000000000000000000000000000000000000000000000000000000008152fd5b60046040517f5e4f2826000000000000000000000000000000000000000000000000000000008152fd5b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57610019600435611fa381610200565b611fab612da2565b611fd5817f00000000000000000000000000000000000000000000000000000000000000006135f7565b7f00000000000000000000000000000000000000000000000000000000000000006135f7565b6004359069ffffffffffffffffffff8216820361021e57565b6024359069ffffffffffffffffffff8216820361021e57565b6044359069ffffffffffffffffffff8216820361021e57565b3461021e5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5761207d611ffb565b612085612014565b61208d61202d565b91612096612da2565b69ffffffffffffffffffff80841690662386f26fc100008211612267578083166005811090811561225c575b506122325783169061012c821161220857151590816121fd575b506121d357611e687f0218c65ca00eb675f88ded3d89b4ff402d73ea8be15c060a7e8a0b0f1b3506f3936121a5612111612bf2565b69ffffffffffffffffffff8581168083528782166020840152908416604090920191909152600a80547fffff00000000000000000000000000000000000000000000000000000000000016909117605087901b73ffffffffffffffffffff00000000000000000000161760a084901b7dffffffffffffffffffff000000000000000000000000000000000000000016179055565b6040805169ffffffffffffffffffff9485168152948416602086015292169183019190915281906060820190565b60046040517fe0dc9368000000000000000000000000000000000000000000000000000000008152fd5b601e915010386120dc565b60046040517fca025cdc000000000000000000000000000000000000000000000000000000008152fd5b60046040517f79f6600a000000000000000000000000000000000000000000000000000000008152fd5b603c915011386120c2565b60046040517fe628cb77000000000000000000000000000000000000000000000000000000008152fd5b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e576004356122cc81610200565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152600760209081526040918290208054600182015460028301546003840154600490940154865193909716835293820152928301919091526060820152608081019190915260a090f35b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e576004356123dc81610200565b6123e4612da2565b60ff60015460a01c1660038110156107a257611cb957611e687fb86c75c9bffca616b2d314cc914f7c3f1d174255b16b941c3f3ededee276d5ef91612463740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff6001541617600155565b6124a88173ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff00000000000000000000000000000000000000006001541617600155565b6040805133815273ffffffffffffffffffffffffffffffffffffffff909216602083015290918291820190565b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e576060600a5469ffffffffffffffffffff604051918181168352818160501c16602084015260a01c166040820152f35b3461021e5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5760043561256e81610200565b6024356044359160643592612581612da2565b8083116126755780841061264b577f3d79b1f4d178dca1291fc0e7778f2ec80a42a6f6ade1163bf0270a7d5062181c93611e68916126126040516125c481612b5a565b86815282602082015283604082015273ffffffffffffffffffffffffffffffffffffffff86166000526005602052604060002090604060029180518455602081015160018501550151910155565b604051948594859094939260609273ffffffffffffffffffffffffffffffffffffffff6080840197168352602083015260408201520152565b60046040517ff23f89a8000000000000000000000000000000000000000000000000000000008152fd5b60046040517f1741c7de000000000000000000000000000000000000000000000000000000008152fd5b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57602060405160328152f35b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5773ffffffffffffffffffffffffffffffffffffffff60043561272981610200565b1660005260086020526020604060002054604051908152f35b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5760043561277d81610200565b612785612da2565b6040517f38d52e0f0000000000000000000000000000000000000000000000000000000081529073ffffffffffffffffffffffffffffffffffffffff6020836004818585165afa9283156105de576000936128f8575b5061280961101c8473ffffffffffffffffffffffffffffffffffffffff166000526004602052604060002090565b166128ce578161289e8261285e7fae9f6e3830561ab2842077b2e30a79f45fb14ebd4e8c7531f3b6314a54b620109573ffffffffffffffffffffffffffffffffffffffff166000526004602052604060002090565b9073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b6040805173ffffffffffffffffffffffffffffffffffffffff928316815292909116602083015281908101611e68565b60046040517ffc973661000000000000000000000000000000000000000000000000000000008152fd5b61291291935060203d6020116113b5576113a68183612b76565b91386127db565b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5773ffffffffffffffffffffffffffffffffffffffff6004356129d881610200565b1660005260096020526020604060002054604051908152f35b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57600435612a2c81610200565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152600660209081526040918290208054600182015460028301546003840154600490940154865193909716835293820152928301919091526060820152608081019190915260a090f35b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5773ffffffffffffffffffffffffffffffffffffffff600435612ae281610200565b16600052600560205260606040600020805490600260018201549101549060405192835260208301526040820152f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff8111612b5557604052565b612b12565b6060810190811067ffffffffffffffff821117612b5557604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612b5557604052565b6040513d6000823e3d90fd5b9081602091031261021e575190565b6040519060a0820182811067ffffffffffffffff821117612b5557604052565b60405190612bff82612b5a565b565b906080600491612c6373ffffffffffffffffffffffffffffffffffffffff825116859073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b6020810151600185015560408101516002850155606081015160038501550151910155565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b91908201809211612cc457565b612c88565b90603282029180830460321490151715612cc457565b91908203918211612cc457565b9081602091031261021e5751612d0181610200565b90565b73ffffffffffffffffffffffffffffffffffffffff809116600052600460205260406000205416908115612d3457565b60046040517f77d36d33000000000000000000000000000000000000000000000000000000008152fd5b69ffffffffffffffffffff600a5460a01c163403612d7857565b60046040517f68b097d0000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff600054163303612dc357565b60046040517f30cd7471000000000000000000000000000000000000000000000000000000008152fd5b60ff60035416612df957565b60046040517f1309a563000000000000000000000000000000000000000000000000000000008152fd5b90801561302357612e548273ffffffffffffffffffffffffffffffffffffffff166000526005602052604060002090565b548110613023576001612e878373ffffffffffffffffffffffffffffffffffffffff166000526005602052604060002090565b01548111612fbd5773ffffffffffffffffffffffffffffffffffffffff604051907f38d52e0f00000000000000000000000000000000000000000000000000000000825260209182816004818589165afa9182156105de578391600093613004575b506040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff871660048201529283916024918391165afa9182156105de57600293612fb493612f8d93612f5c93600092612fe7575b5050612cb7565b612f868573ffffffffffffffffffffffffffffffffffffffff166000526008602052604060002090565b5490612cb7565b9273ffffffffffffffffffffffffffffffffffffffff166000526005602052604060002090565b015410612fbd57565b60046040517f38d0ba26000000000000000000000000000000000000000000000000000000008152fd5b612ffd9250803d106105d7576105c88183612b76565b3880612f55565b61301c919350823d84116113b5576113a68183612b76565b9138612ee9565b60046040517ff4ee239f000000000000000000000000000000000000000000000000000000008152fd5b69ffffffffffffffffffff600a54168101809111612cc457421061306d57565b60046040517f8fae99f9000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff1633141590816130e8575b506130be57565b60046040517fd051d18f000000000000000000000000000000000000000000000000000000008152fd5b9050600a5469ffffffffffffffffffff908181168301809311612cc45760501c168101809111612cc4574210386130b7565b9081602091031261021e5751801515810361021e5790565b90916000916131488380808086896108fcf11590565b613153575b50505050565b73ffffffffffffffffffffffffffffffffffffffff16803b1561326557604051937fd0e30db0000000000000000000000000000000000000000000000000000000008552838560048186865af19384156105de5761320c95602095613252575b506040518096819582947fa9059cbb000000000000000000000000000000000000000000000000000000008452600484016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af180156105de57613223575b80808061314d565b6132449060203d60201161324b575b61323c8183612b76565b81019061311a565b503861321b565b503d613232565b806105f061325f92612b41565b386131b3565b8280fd5b61314882849395600080809781948294f11590565b91907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff93848216809582161460001461332b5750833b1561021e57600080946024604051809781937f2e1a7d4d0000000000000000000000000000000000000000000000000000000083528860048401525af19384156105de57612bff9461331c575b50613132565b61332590612b41565b38613316565b9050612bff9350613395565b3d15613390573d9067ffffffffffffffff8211612b55576040519161338460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184612b76565b82523d6000602084013e565b606090565b919091803b1561346c576040517fa9059cbb000000000000000000000000000000000000000000000000000000006020820190815273ffffffffffffffffffffffffffffffffffffffff94909416602482015260448082019390935291825260009283928390613406606482612b76565b51925af1613412613337565b901561344257805180613423575050565b816020806134389361343c950101910161311a565b1590565b61344257565b60046040517ff1568f95000000000000000000000000000000000000000000000000000000008152fd5b60046040517f09ee12d5000000000000000000000000000000000000000000000000000000008152fd5b90919073ffffffffffffffffffffffffffffffffffffffff9081167f0000000000000000000000000000000000000000000000000000000000000000821681811461357b575b5091600084926134f38360209761354b9997613709565b6040518097819682957f6e553f650000000000000000000000000000000000000000000000000000000084526004840190929173ffffffffffffffffffffffffffffffffffffffff6020916040840195845216910152565b0393165af19081156105de57600091613562575090565b612d01915060203d6020116105d7576105c88183612b76565b9094929391813b1561021e57600085926004604051809881937fd0e30db00000000000000000000000000000000000000000000000000000000083525af19081156105de576134f3838561354b996020996000966135e4575b50955050959750509294506134dc565b806105f06135f192612b41565b386135d4565b6040517fe12f3a6100000000000000000000000000000000000000000000000000000000815230600482015260209273ffffffffffffffffffffffffffffffffffffffff9290921691908381602481865afa9081156105de576000916136ec575b50806136645750505050565b6040517faad3ec9600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9290921660048301526024820152908290829060449082906000905af180156105de576136ce575b808061314d565b816136e492903d106105d7576105c88183612b76565b5038806136c7565b6137039150843d86116105d7576105c88183612b76565b38613658565b6040517f095ea7b3000000000000000000000000000000000000000000000000000000006020820181815273ffffffffffffffffffffffffffffffffffffffff851660248401526044808401969096529482529390927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09161378c606486612b76565b60008073ffffffffffffffffffffffffffffffffffffffff86169287519082855af1906137b7613337565b82613838575b508161382d575b50156137d2575b5050505050565b604051602081019590955273ffffffffffffffffffffffffffffffffffffffff16602485015260006044850152606490810184526138239361381e916138189082612b76565b8261386a565b61386a565b38808080806137cb565b90503b1515386137c4565b80519192508115918215613850575b505090386137bd565b613863925060208091830101910161311a565b3880613847565b60008073ffffffffffffffffffffffffffffffffffffffff6138a193169360208151910182865af161389a613337565b9083613903565b80519081151591826138e8575b50506138b75750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b6138fb925060208091830101910161311a565b1538806138ae565b90613942575080511561391857805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b8151158061399a575b613953575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b1561394b56fea264697066735822122055543c545d8892df22eb4ff51fddf98a9a3b4241c1275d4b248d17b0cc71942264736f6c634300081800330000000000000000000000002c64e6ee1dd9fc2a0db6a6b1aa2c3f163c7a2c780000000000000000000000004300000000000000000000000000000000000004000000000000000000000000430000000000000000000000000000000000000300000000000000000000000000000000007fe8d7666bb0da2a5d13f72b8dabab00000000000000000000000043000000000000000000000000000000000000020000000000000000000000002536fe9ab3f511540f2f9e2ec2a805005c3dd8000000000000000000000000004066b9bd584b5fa88897194dabe3a37883ac35f7
Deployed Bytecode
0x6080604052600436101561001b575b361561001957600080fd5b005b60003560e01c80630b0fd47e146101fb5780631e9a6950146101f657806323452b9c146101f15780632bb5a9e6146101ec57806331a0edec146101e757806336566f06146101e25780633e567539146101dd57806347e7ef24146101d85780635002b2ba146101d35780635358fbda146101ce57806353d790b6146101c95780635b6ac011146101c45780635c975abb146101bf5780637200b829146101ba5780637762df25146101b55780638da5cb5b146101b0578063999927df146101ab5780639ab4ffcf146101a65780639bc3f269146101a1578063ad5c46481461019c578063c0b6f56114610197578063c64fe82814610192578063e22e8e011461018d578063e97c2f5d14610188578063eb3349b914610183578063ee6a934c1461017e578063f01f7ce414610179578063f3f4370314610174578063fc7e286d1461016f5763ff98c1e70361000e57612a92565b6129f1565b612988565b612919565b612742565b6126d9565b61269f565b612533565b6124d5565b6123a1565b612332565b612291565b612046565b611f65565b611f13565b611ec1565b611d51565b611d10565b611c07565b6117aa565b61140e565b610f2d565b610ab8565b6109b3565b61088b565b61081c565b6107d1565b610656565b61028e565b610223565b73ffffffffffffffffffffffffffffffffffffffff81160361021e57565b600080fd5b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57602060043561026081610200565b73ffffffffffffffffffffffffffffffffffffffff8091166000526004825260406000205416604051908152f35b6040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5760048035906102c782610200565b602435916002805414610623576102e19060028055612d04565b906102ea612d5e565b60016103163373ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b01546105fc5773ffffffffffffffffffffffffffffffffffffffff90817f00000000000000000000000000000000007fe8d7666bb0da2a5d13f72b8dabab16803b1561021e5785517fda3e8ce400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851683820190815233602082015230604082015260608101879052909160009183919082908490829060800103925af180156105de576105e3575b506020855180937f4cdad506000000000000000000000000000000000000000000000000000000008252818061040e898783019190602083019252565b039187165afa9182156105de576000926105ad575b50811561058657507f2e35b251a7f84ecdc4395881eedc1d3ae2e9f05ad4309b3e7d770537757b152f93610579916104e061046d600a5469ffffffffffffffffffff9060a01c1690565b69ffffffffffffffffffff610480612bd2565b73ffffffffffffffffffffffffffffffffffffffff881681529188602084015284868401524260608401521660808201526104db3373ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b612c01565b61050a8473ffffffffffffffffffffffffffffffffffffffff166000526009602052604060002090565b610515828254612cb7565b9055600a5460a01c69ffffffffffffffffffff165b915194859433869360809369ffffffffffffffffffff9397969260a087019873ffffffffffffffffffffffffffffffffffffffff80921688521660208701526040860152606085015216910152565b0390a16100196001600255565b84517f1c2510c7000000000000000000000000000000000000000000000000000000008152fd5b6105d091925060203d6020116105d7575b6105c88183612b76565b810190612bc3565b9038610423565b503d6105be565b612bb7565b806105f06105f692612b41565b8061064b565b386103d1565b83517ff69b65f6000000000000000000000000000000000000000000000000000000008152fd5b5082517f1bbee726000000000000000000000000000000000000000000000000000000008152fd5b600091031261021e57565b3461021e576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126107955761068e612da2565b60ff60015460a01c16600381101561076857801561073e57806106b2600192610798565b1461070c575b6106e57fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff60015416600155565b7f8eca980489e87f7dba4f26917aa4bfc906eb3f2b4f7b4b9fd0ff2b8bb3e21ae38180a180f35b6107397fffffffffffffffffffffffff000000000000000000000000000000000000000060015416600155565b6106b8565b60046040517fccf69db7000000000000000000000000000000000000000000000000000000008152fd5b6024827f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b80fd5b600311156107a257565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5760ff60015460a01c1660405160038210156107a2576020918152f35b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004300000000000000000000000000000000000003168152f35b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e576108c2612da2565b60035460ff811615610956575060035460ff81161561092c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166003557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60046040517f6cd60201000000000000000000000000000000000000000000000000000000008152fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00600191610982612ded565b16176003557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b3461021e576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610795576109eb612da2565b60ff60015460a01c16600381101561076857600203610a8e57610a317fffffffffffffffffffffffff000000000000000000000000000000000000000060005416600055565b610a5e7fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff60015416600155565b604051600081527f3edd90e7770f06fafde38004653b33870066c33bfc923ff6102acd601f85dfbc90602090a180f35b60046040517f045c5122000000000000000000000000000000000000000000000000000000008152fd5b6040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e576004803590610af182610200565b602435906002805414610f065760028055610b0a612ded565b73ffffffffffffffffffffffffffffffffffffffff91827f00000000000000000000000043000000000000000000000000000000000000041683851614610ede57610b5484612d04565b91610b5d612d5e565b610b7b610b74610b6c84612cc9565b612710900490565b8093612cdf565b90610b868285612e23565b6001610bb23373ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b0154610eb757847f00000000000000000000000000000000007fe8d7666bb0da2a5d13f72b8dabab1695863b1561021e5787517fda3e8ce40000000000000000000000000000000000000000000000000000000080825273ffffffffffffffffffffffffffffffffffffffff83168483019081523360208201523060408201526060810186905260009991939192908a9082908190608001038183875af180156105de57610ea4575b50885473ffffffffffffffffffffffffffffffffffffffff1692823b15610ea0578a5190815273ffffffffffffffffffffffffffffffffffffffff918216858201908152336020820152919093166040820152606081018690528891839182908490829060800103925af180156105de57610e8d575b506020875180967fef8b30f70000000000000000000000000000000000000000000000000000000082528180610d0e878783019190602083019252565b039188165afa9485156105de578695610e6c575b508415610e45575094610e1b610e35927faeeb0857c3a75fe5aad3955b0147e40f58826a77f6f2bd632791e0c387f0b3e49697610ddf610d71600a5469ffffffffffffffffffff9060a01c1690565b69ffffffffffffffffffff610d84612bd2565b73ffffffffffffffffffffffffffffffffffffffff8a168152918460208401528a888401524260608401521660808201526104db3373ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b610e098673ffffffffffffffffffffffffffffffffffffffff166000526008602052604060002090565b610e14828254612cb7565b9055612cb7565b9361052a600a5469ffffffffffffffffffff9060a01c1690565b0390a1610e426001600255565b80f35b86517f2da2a1e1000000000000000000000000000000000000000000000000000000008152fd5b610e8691955060203d6020116105d7576105c88183612b76565b9338610d22565b806105f0610e9a92612b41565b38610cd1565b8980fd5b806105f0610eb192612b41565b38610c5b565b86517f3434dd01000000000000000000000000000000000000000000000000000000008152fd5b5083517f16c6503f000000000000000000000000000000000000000000000000000000008152fd5b83517f1bbee726000000000000000000000000000000000000000000000000000000008152fd5b3461021e576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e576004908135610f6b81610200565b60028054146113e557600280556001610fa48273ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b015480156113bc57610fec6003610fdb8473ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b0154610fe68161304d565b83613097565b61104f61103661101c8473ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b6040517f38d52e0f000000000000000000000000000000000000000000000000000000008152909373ffffffffffffffffffffffffffffffffffffffff851681838881845afa9283156105de5760009361138d575b5060026110d18673ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b0154876110fe8773ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b01549761115561110c612bd2565b600081526000868201526000604082015260006060820152600060808201526104db8973ffffffffffffffffffffffffffffffffffffffff166000526007602052604060002090565b61117f8873ffffffffffffffffffffffffffffffffffffffff166000526009602052604060002090565b61118a838254612cdf565b9055604080517fba0876520000000000000000000000000000000000000000000000000000000081528281018881523060208201819052928101929092529085908290819060600103816000885af19081156105de57600091611370575b50611215819a337f0000000000000000000000004300000000000000000000000000000000000004613132565b808310611299575050505050936105799161125282857fbe7bc3899b370584fa4121ff891418505639d5991dee9eba19287e4793a6cf979861327e565b604051948594338691608093969594919660a084019773ffffffffffffffffffffffffffffffffffffffff9283809216865216602085015216604083015260608201520152565b90929594919398506112ac84888461327e565b8860405180947f18160ddd00000000000000000000000000000000000000000000000000000000825281895afa9586156105de578461057997611343957fbe7bc3899b370584fa4121ff891418505639d5991dee9eba19287e4793a6cf979c600092611353575b50506113495760005461133d925073ffffffffffffffffffffffffffffffffffffffff1692612cdf565b91613395565b90611252565b61133d9192612cdf565b6113699250803d106105d7576105c88183612b76565b3880611313565b6113879150853d87116105d7576105c88183612b76565b386111e8565b6113ae919350823d84116113b5575b6113a68183612b76565b810190612cec565b91386110a4565b503d61139c565b836040517f299597ac000000000000000000000000000000000000000000000000000000008152fd5b826040517f1bbee726000000000000000000000000000000000000000000000000000000008152fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57600480359060028054146117825760028055611452612ded565b7f000000000000000000000000430000000000000000000000000000000000000461147c81612d04565b906114b16114ab61149c600a5469ffffffffffffffffffff9060a01c1690565b69ffffffffffffffffffff1690565b85612cb7565b3403611759576114cd6114c6610b6c86612cc9565b8095612cdf565b906114d88284612e23565b60016115043373ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b015461173057611535908561152e60005473ffffffffffffffffffffffffffffffffffffffff1690565b5a92613269565b604051927fef8b30f700000000000000000000000000000000000000000000000000000000845260208480611571858583019190602083019252565b038173ffffffffffffffffffffffffffffffffffffffff87165afa9384156105de5760009461170f575b5083156116e757506116817faeeb0857c3a75fe5aad3955b0147e40f58826a77f6f2bd632791e0c387f0b3e494610579926116576115e8600a5469ffffffffffffffffffff9060a01c1690565b69ffffffffffffffffffff6115fb612bd2565b73ffffffffffffffffffffffffffffffffffffffff89168152918460208401528960408401524260608401521660808201526104db3373ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b610e098573ffffffffffffffffffffffffffffffffffffffff166000526008602052604060002090565b9261169b600a5469ffffffffffffffffffff9060a01c1690565b6040805133815273ffffffffffffffffffffffffffffffffffffffff9095166020860152840194909452606083015269ffffffffffffffffffff909216608082015290819060a0820190565b6040517f2da2a1e1000000000000000000000000000000000000000000000000000000008152fd5b61172991945060203d6020116105d7576105c88183612b76565b923861159b565b836040517f3434dd01000000000000000000000000000000000000000000000000000000008152fd5b826040517f68b097d0000000000000000000000000000000000000000000000000000000008152fd5b6040517f1bbee726000000000000000000000000000000000000000000000000000000008152fd5b3461021e576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5760049081356117e881610200565b60028054146113e5576002805560016118218273ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b01548015611bde576118586003610fdb8473ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b61188861103661101c8473ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b9273ffffffffffffffffffffffffffffffffffffffff9081851691604051967f38d52e0f00000000000000000000000000000000000000000000000000000000885282888281875afa9788156105de57600098611bbf575b50600261190d8773ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b0154604051917fef8b30f700000000000000000000000000000000000000000000000000000000835284838061194a8a8583019190602083019252565b0381895afa9283156105de57600093611ba0575b508061198a8973ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b0154926119e1611998612bd2565b600081526000888201526000604082015260006060820152600060808201526104db8b73ffffffffffffffffffffffffffffffffffffffff166000526006602052604060002090565b611a0b8a73ffffffffffffffffffffffffffffffffffffffff166000526008602052604060002090565b611a16898254612cdf565b9055611a447f0000000000000000000000004300000000000000000000000000000000000004943386613132565b8210611ac657505050505093611a8183837f7eed4a581ebd679621ba6cad2ad0516cd66821e5853dd38d200f5396931ae380976105799594613496565b6040805133815273ffffffffffffffffffffffffffffffffffffffff9586166020820152959094169385019390935260608401526080830191909152819060a0820190565b6040517fb3d7f6b900000000000000000000000000000000000000000000000000000000815290810191825298939592949086908a9081906020010381845afa9283156105de577f7eed4a581ebd679621ba6cad2ad0516cd66821e5853dd38d200f5396931ae3809961057997600095611b81575b50508394611b4b89868584613496565b9681811690851603611b725750611b6d93611b6591612cdf565b905a92613269565b611252565b9250611b6d9361133d91612cdf565b611b98929550803d106105d7576105c88183612b76565b923880611b3b565b611bb8919350853d87116105d7576105c88183612b76565b913861195e565b611bd7919850833d85116113b5576113a68183612b76565b96386118e0565b836040517f3d89895d000000000000000000000000000000000000000000000000000000008152fd5b3461021e576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261079557611c3f612da2565b60015460ff8160a01c166003811015611ce357611cb9577fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674020000000000000000000000000000000000000000176001557f3ff05a45e46337fa1cbf20996d2eeb927280bce099f37252bcca1040609604ec8180a180f35b60046040517f74ed79ae000000000000000000000000000000000000000000000000000000008152fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57602060ff600354166040519015158152f35b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57600154600160ff8260a01c16611d9581610798565b03611e975773ffffffffffffffffffffffffffffffffffffffff163303611e6d57600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055611e0d7fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff60015416600155565b611e3a7fffffffffffffffffffffffff000000000000000000000000000000000000000060015416600155565b6040513381527f3edd90e7770f06fafde38004653b33870066c33bfc923ff6102acd601f85dfbc9080602081015b0390a1005b60046040517fafdcfb92000000000000000000000000000000000000000000000000000000008152fd5b60046040517f5e4f2826000000000000000000000000000000000000000000000000000000008152fd5b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57610019600435611fa381610200565b611fab612da2565b611fd5817f00000000000000000000000043000000000000000000000000000000000000046135f7565b7f00000000000000000000000043000000000000000000000000000000000000036135f7565b6004359069ffffffffffffffffffff8216820361021e57565b6024359069ffffffffffffffffffff8216820361021e57565b6044359069ffffffffffffffffffff8216820361021e57565b3461021e5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5761207d611ffb565b612085612014565b61208d61202d565b91612096612da2565b69ffffffffffffffffffff80841690662386f26fc100008211612267578083166005811090811561225c575b506122325783169061012c821161220857151590816121fd575b506121d357611e687f0218c65ca00eb675f88ded3d89b4ff402d73ea8be15c060a7e8a0b0f1b3506f3936121a5612111612bf2565b69ffffffffffffffffffff8581168083528782166020840152908416604090920191909152600a80547fffff00000000000000000000000000000000000000000000000000000000000016909117605087901b73ffffffffffffffffffff00000000000000000000161760a084901b7dffffffffffffffffffff000000000000000000000000000000000000000016179055565b6040805169ffffffffffffffffffff9485168152948416602086015292169183019190915281906060820190565b60046040517fe0dc9368000000000000000000000000000000000000000000000000000000008152fd5b601e915010386120dc565b60046040517fca025cdc000000000000000000000000000000000000000000000000000000008152fd5b60046040517f79f6600a000000000000000000000000000000000000000000000000000000008152fd5b603c915011386120c2565b60046040517fe628cb77000000000000000000000000000000000000000000000000000000008152fd5b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e576004356122cc81610200565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152600760209081526040918290208054600182015460028301546003840154600490940154865193909716835293820152928301919091526060820152608081019190915260a090f35b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004300000000000000000000000000000000000004168152f35b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e576004356123dc81610200565b6123e4612da2565b60ff60015460a01c1660038110156107a257611cb957611e687fb86c75c9bffca616b2d314cc914f7c3f1d174255b16b941c3f3ededee276d5ef91612463740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff6001541617600155565b6124a88173ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff00000000000000000000000000000000000000006001541617600155565b6040805133815273ffffffffffffffffffffffffffffffffffffffff909216602083015290918291820190565b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e576060600a5469ffffffffffffffffffff604051918181168352818160501c16602084015260a01c166040820152f35b3461021e5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5760043561256e81610200565b6024356044359160643592612581612da2565b8083116126755780841061264b577f3d79b1f4d178dca1291fc0e7778f2ec80a42a6f6ade1163bf0270a7d5062181c93611e68916126126040516125c481612b5a565b86815282602082015283604082015273ffffffffffffffffffffffffffffffffffffffff86166000526005602052604060002090604060029180518455602081015160018501550151910155565b604051948594859094939260609273ffffffffffffffffffffffffffffffffffffffff6080840197168352602083015260408201520152565b60046040517ff23f89a8000000000000000000000000000000000000000000000000000000008152fd5b60046040517f1741c7de000000000000000000000000000000000000000000000000000000008152fd5b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57602060405160328152f35b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5773ffffffffffffffffffffffffffffffffffffffff60043561272981610200565b1660005260086020526020604060002054604051908152f35b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5760043561277d81610200565b612785612da2565b6040517f38d52e0f0000000000000000000000000000000000000000000000000000000081529073ffffffffffffffffffffffffffffffffffffffff6020836004818585165afa9283156105de576000936128f8575b5061280961101c8473ffffffffffffffffffffffffffffffffffffffff166000526004602052604060002090565b166128ce578161289e8261285e7fae9f6e3830561ab2842077b2e30a79f45fb14ebd4e8c7531f3b6314a54b620109573ffffffffffffffffffffffffffffffffffffffff166000526004602052604060002090565b9073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b6040805173ffffffffffffffffffffffffffffffffffffffff928316815292909116602083015281908101611e68565b60046040517ffc973661000000000000000000000000000000000000000000000000000000008152fd5b61291291935060203d6020116113b5576113a68183612b76565b91386127db565b3461021e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57602060405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000007fe8d7666bb0da2a5d13f72b8dabab168152f35b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5773ffffffffffffffffffffffffffffffffffffffff6004356129d881610200565b1660005260096020526020604060002054604051908152f35b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e57600435612a2c81610200565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152600660209081526040918290208054600182015460028301546003840154600490940154865193909716835293820152928301919091526060820152608081019190915260a090f35b3461021e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261021e5773ffffffffffffffffffffffffffffffffffffffff600435612ae281610200565b16600052600560205260606040600020805490600260018201549101549060405192835260208301526040820152f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff8111612b5557604052565b612b12565b6060810190811067ffffffffffffffff821117612b5557604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612b5557604052565b6040513d6000823e3d90fd5b9081602091031261021e575190565b6040519060a0820182811067ffffffffffffffff821117612b5557604052565b60405190612bff82612b5a565b565b906080600491612c6373ffffffffffffffffffffffffffffffffffffffff825116859073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b6020810151600185015560408101516002850155606081015160038501550151910155565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b91908201809211612cc457565b612c88565b90603282029180830460321490151715612cc457565b91908203918211612cc457565b9081602091031261021e5751612d0181610200565b90565b73ffffffffffffffffffffffffffffffffffffffff809116600052600460205260406000205416908115612d3457565b60046040517f77d36d33000000000000000000000000000000000000000000000000000000008152fd5b69ffffffffffffffffffff600a5460a01c163403612d7857565b60046040517f68b097d0000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff600054163303612dc357565b60046040517f30cd7471000000000000000000000000000000000000000000000000000000008152fd5b60ff60035416612df957565b60046040517f1309a563000000000000000000000000000000000000000000000000000000008152fd5b90801561302357612e548273ffffffffffffffffffffffffffffffffffffffff166000526005602052604060002090565b548110613023576001612e878373ffffffffffffffffffffffffffffffffffffffff166000526005602052604060002090565b01548111612fbd5773ffffffffffffffffffffffffffffffffffffffff604051907f38d52e0f00000000000000000000000000000000000000000000000000000000825260209182816004818589165afa9182156105de578391600093613004575b506040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff871660048201529283916024918391165afa9182156105de57600293612fb493612f8d93612f5c93600092612fe7575b5050612cb7565b612f868573ffffffffffffffffffffffffffffffffffffffff166000526008602052604060002090565b5490612cb7565b9273ffffffffffffffffffffffffffffffffffffffff166000526005602052604060002090565b015410612fbd57565b60046040517f38d0ba26000000000000000000000000000000000000000000000000000000008152fd5b612ffd9250803d106105d7576105c88183612b76565b3880612f55565b61301c919350823d84116113b5576113a68183612b76565b9138612ee9565b60046040517ff4ee239f000000000000000000000000000000000000000000000000000000008152fd5b69ffffffffffffffffffff600a54168101809111612cc457421061306d57565b60046040517f8fae99f9000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff1633141590816130e8575b506130be57565b60046040517fd051d18f000000000000000000000000000000000000000000000000000000008152fd5b9050600a5469ffffffffffffffffffff908181168301809311612cc45760501c168101809111612cc4574210386130b7565b9081602091031261021e5751801515810361021e5790565b90916000916131488380808086896108fcf11590565b613153575b50505050565b73ffffffffffffffffffffffffffffffffffffffff16803b1561326557604051937fd0e30db0000000000000000000000000000000000000000000000000000000008552838560048186865af19384156105de5761320c95602095613252575b506040518096819582947fa9059cbb000000000000000000000000000000000000000000000000000000008452600484016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af180156105de57613223575b80808061314d565b6132449060203d60201161324b575b61323c8183612b76565b81019061311a565b503861321b565b503d613232565b806105f061325f92612b41565b386131b3565b8280fd5b61314882849395600080809781948294f11590565b91907f000000000000000000000000430000000000000000000000000000000000000473ffffffffffffffffffffffffffffffffffffffff93848216809582161460001461332b5750833b1561021e57600080946024604051809781937f2e1a7d4d0000000000000000000000000000000000000000000000000000000083528860048401525af19384156105de57612bff9461331c575b50613132565b61332590612b41565b38613316565b9050612bff9350613395565b3d15613390573d9067ffffffffffffffff8211612b55576040519161338460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184612b76565b82523d6000602084013e565b606090565b919091803b1561346c576040517fa9059cbb000000000000000000000000000000000000000000000000000000006020820190815273ffffffffffffffffffffffffffffffffffffffff94909416602482015260448082019390935291825260009283928390613406606482612b76565b51925af1613412613337565b901561344257805180613423575050565b816020806134389361343c950101910161311a565b1590565b61344257565b60046040517ff1568f95000000000000000000000000000000000000000000000000000000008152fd5b60046040517f09ee12d5000000000000000000000000000000000000000000000000000000008152fd5b90919073ffffffffffffffffffffffffffffffffffffffff9081167f0000000000000000000000004300000000000000000000000000000000000004821681811461357b575b5091600084926134f38360209761354b9997613709565b6040518097819682957f6e553f650000000000000000000000000000000000000000000000000000000084526004840190929173ffffffffffffffffffffffffffffffffffffffff6020916040840195845216910152565b0393165af19081156105de57600091613562575090565b612d01915060203d6020116105d7576105c88183612b76565b9094929391813b1561021e57600085926004604051809881937fd0e30db00000000000000000000000000000000000000000000000000000000083525af19081156105de576134f3838561354b996020996000966135e4575b50955050959750509294506134dc565b806105f06135f192612b41565b386135d4565b6040517fe12f3a6100000000000000000000000000000000000000000000000000000000815230600482015260209273ffffffffffffffffffffffffffffffffffffffff9290921691908381602481865afa9081156105de576000916136ec575b50806136645750505050565b6040517faad3ec9600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9290921660048301526024820152908290829060449082906000905af180156105de576136ce575b808061314d565b816136e492903d106105d7576105c88183612b76565b5038806136c7565b6137039150843d86116105d7576105c88183612b76565b38613658565b6040517f095ea7b3000000000000000000000000000000000000000000000000000000006020820181815273ffffffffffffffffffffffffffffffffffffffff851660248401526044808401969096529482529390927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09161378c606486612b76565b60008073ffffffffffffffffffffffffffffffffffffffff86169287519082855af1906137b7613337565b82613838575b508161382d575b50156137d2575b5050505050565b604051602081019590955273ffffffffffffffffffffffffffffffffffffffff16602485015260006044850152606490810184526138239361381e916138189082612b76565b8261386a565b61386a565b38808080806137cb565b90503b1515386137c4565b80519192508115918215613850575b505090386137bd565b613863925060208091830101910161311a565b3880613847565b60008073ffffffffffffffffffffffffffffffffffffffff6138a193169360208151910182865af161389a613337565b9083613903565b80519081151591826138e8575b50506138b75750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b6138fb925060208091830101910161311a565b1538806138ae565b90613942575080511561391857805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b8151158061399a575b613953575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b1561394b56fea264697066735822122055543c545d8892df22eb4ff51fddf98a9a3b4241c1275d4b248d17b0cc71942264736f6c63430008180033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000002c64e6ee1dd9fc2a0db6a6b1aa2c3f163c7a2c780000000000000000000000004300000000000000000000000000000000000004000000000000000000000000430000000000000000000000000000000000000300000000000000000000000000000000007fe8d7666bb0da2a5d13f72b8dabab00000000000000000000000043000000000000000000000000000000000000020000000000000000000000002536fe9ab3f511540f2f9e2ec2a805005c3dd8000000000000000000000000004066b9bd584b5fa88897194dabe3a37883ac35f7
-----Decoded View---------------
Arg [0] : _owner (address): 0x2C64e6Ee1Dd9Fc2a0Db6a6B1aa2c3f163C7A2C78
Arg [1] : _weth (address): 0x4300000000000000000000000000000000000004
Arg [2] : _usdb (address): 0x4300000000000000000000000000000000000003
Arg [3] : _transferManager (address): 0x00000000007FE8d7666BB0da2A5D13f72b8dABaB
Arg [4] : _blast (address): 0x4300000000000000000000000000000000000002
Arg [5] : _blastPoints (address): 0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800
Arg [6] : _blastPointsOperator (address): 0x4066b9BD584b5FA88897194dAbE3a37883AC35F7
-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000002c64e6ee1dd9fc2a0db6a6b1aa2c3f163c7a2c78
Arg [1] : 0000000000000000000000004300000000000000000000000000000000000004
Arg [2] : 0000000000000000000000004300000000000000000000000000000000000003
Arg [3] : 00000000000000000000000000000000007fe8d7666bb0da2a5d13f72b8dabab
Arg [4] : 0000000000000000000000004300000000000000000000000000000000000002
Arg [5] : 0000000000000000000000002536fe9ab3f511540f2f9e2ec2a805005c3dd800
Arg [6] : 0000000000000000000000004066b9bd584b5fa88897194dabe3a37883ac35f7
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.