Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
CurveDolaLPHelper
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 10000 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; import {IMarket} from "src/interfaces/IMarket.sol"; import {Sweepable, SafeERC20, IERC20} from "src/util/Sweepable.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; import {ICurvePool} from "src/interfaces/ICurvePool.sol"; import {IYearnVaultV2} from "src/interfaces/IYearnVaultV2.sol"; /** * @title CurveLP Helper for ALE and Market * @notice This contract is a generalized ALE helper contract for a curve pool with 2 and 3 coins with DOLA. Also support YearnV2 vaults for this LP. * @dev This contract is used by the ALE to interact with Dola Curve pools or YearnV2 Curve vaults and market. * Can also be used by anyone to perform add/remove liquidity from and to DOLA and deposit/withdraw operations. **/ contract CurveDolaLPHelper is Sweepable, IMultiMarketTransformHelper { using SafeERC20 for IERC20; error InsufficientLP(); error InsufficientShares(); error MarketNotSet(address market); error NotImplemented(); struct Pool { ICurvePool pool; uint128 dolaIndex; uint128 length; IYearnVaultV2 vault; } event MarketSet( address indexed market, uint128 dolaIndex, address indexed pool, address indexed yearnVault ); event MarketRemoved(address indexed market); IERC20 public immutable DOLA; /// @notice Mapping of market addresses to their associated Curve Pools. mapping(address => Pool) public markets; /** @dev Constructor @param _gov The address of Inverse Finance governance @param _guardian The address of the guardian **/ constructor( address _gov, address _guardian, address _dola ) Sweepable(_gov, _guardian) { DOLA = IERC20(_dola); } /** * @notice Deposits DOLA into the Curve Pool and returns the received LP token. * @dev Used by the ALE but can be called by anyone. * @param amount The amount of underlying token to be deposited. * @param data The encoded address of the market. * @return collateralAmount The amount of LP token received. */ function transformToCollateral( uint256 amount, bytes calldata data ) external override returns (uint256 collateralAmount) { collateralAmount = transformToCollateral(amount, msg.sender, data); } /** * @notice Deposits DOLA into the Curve Pool and returns the received LP token or Yearn token. * @dev Use custom recipient address. * @param amount The amount of DOLA to be deposited. * @param recipient The address on behalf of which the collateralAmount are deposited. * @param data The encoded address of the market. * @return collateralAmount The amount of LP or Yearn token received. */ function transformToCollateral( uint256 amount, address recipient, bytes calldata data ) public override returns (uint256 collateralAmount) { (address market, uint256 minMint) = abi.decode( data, (address, uint256) ); _revertIfMarketNotSet(market); IYearnVaultV2 vault = markets[market].vault; // If vault is set, add DOLA liquidity to Curve Pool and then deposit the LP token into the Yearn Vault if (address(vault) != address(0)) { uint256 lpAmount = _addLiquidity( market, amount, minMint, address(this) ); IERC20(address(markets[market].pool)).approve( address(vault), lpAmount ); return vault.deposit(lpAmount, recipient); } else { // Just add DOLA liquidity to the pool return _addLiquidity(market, amount, minMint, recipient); } } /** * @notice Redeems the LP or Yearn token for DOLA. * @dev Used by the ALE but can be called by anyone. * @param amount The amount of LP or Yearn token to be redeemed. * @param data The encoded address of the market. * @return dolaAmount The amount of DOLA redeemed. */ function transformFromCollateral( uint256 amount, bytes calldata data ) external override returns (uint256 dolaAmount) { dolaAmount = transformFromCollateral(amount, msg.sender, data); } /** * @notice Redeems Collateral for DOLA. * @dev Use custom recipient address. * @param amount The amount of LP or Yearn Token to be redeemed. * @param recipient The address to which the underlying token is transferred. * @param data The encoded address of the market. * @return dolaAmount The amount of DOLA redeemed. */ function transformFromCollateral( uint256 amount, address recipient, bytes calldata data ) public override returns (uint256 dolaAmount) { (address market, uint256 minOut) = abi.decode(data, (address, uint256)); _revertIfMarketNotSet(market); ICurvePool pool = markets[market].pool; IYearnVaultV2 vault = markets[market].vault; uint128 dolaIndex = markets[market].dolaIndex; uint256 lpAmount; // If vault is set, withdraw LP token from the Yearn Vault and then remove liquidity from the pool if (address(vault) != address(0)) { IERC20(address(vault)).safeTransferFrom( msg.sender, address(this), amount ); lpAmount = vault.withdraw(amount); _reimbourseSharesLeft(vault, recipient); } else { // Just remove liquidity from the pool IERC20(address(pool)).safeTransferFrom( msg.sender, address(this), amount ); lpAmount = amount; } return _removeLiquidity(pool, lpAmount, dolaIndex, minOut, recipient); } /** * @notice Convert DOLA into LP or Yearn token and deposit the received amount for recipient. * @param assets The amount of DOLA to be converted. * @param recipient The address on behalf of which the LP or Yearn are deposited. * @param data The encoded address of the market. * @return collateralAmount The amount of collateral deposited into the market. */ function transformToCollateralAndDeposit( uint256 assets, address recipient, bytes calldata data ) external override returns (uint256) { (address market, ) = abi.decode(data, (address, uint256)); _revertIfMarketNotSet(market); // Convert DOLA to LP or Yearn token uint256 amount = transformToCollateral(assets, address(this), data); IYearnVaultV2 vault = markets[market].vault; uint256 actualAmount; address collateral; // If Vault is set, deposit the Yearn token into the market if (address(vault) != address(0)) { collateral = address(vault); actualAmount = vault.balanceOf(address(this)); } else { // Deposit the LP token into the market collateral = address(markets[market].pool); actualAmount = IERC20(collateral).balanceOf(address(this)); } if (amount > actualAmount) revert InsufficientShares(); IERC20(collateral).approve(market, actualAmount); IMarket(market).deposit(recipient, actualAmount); return actualAmount; } /** * @notice Withdraw the collateral from the market then convert to DOLA. * @param amount The amount of LP or Yearn token to be withdrawn from the market. * @param recipient The address to which DOLA is transferred. * @param permit The permit data for the Market. * @param data The encoded address of the market. * @return dolaAmount The amount of DOLA redeemed. */ function withdrawAndTransformFromCollateral( uint256 amount, address recipient, Permit calldata permit, bytes calldata data ) external override returns (uint256 dolaAmount) { (address market, uint256 minOut) = abi.decode(data, (address, uint256)); _revertIfMarketNotSet(market); IMarket(market).withdrawOnBehalf( msg.sender, amount, permit.deadline, permit.v, permit.r, permit.s ); ICurvePool pool = markets[market].pool; IYearnVaultV2 vault = markets[market].vault; // Withdraw from the vault if it is set and then remove liquidity from the pool if (address(vault) != address(0)) { amount = vault.withdraw(amount); _reimbourseSharesLeft(vault, recipient); } // Just remove liquidity from the pool if (IERC20(address(pool)).balanceOf(address(this)) < amount) revert InsufficientLP(); return _removeLiquidity( pool, amount, markets[market].dolaIndex, minOut, recipient ); } function _addLiquidity( address market, uint256 amount, uint256 minMint, address recipient ) internal returns (uint256 lpAmount) { DOLA.safeTransferFrom(msg.sender, address(this), amount); uint128 dolaIndex = markets[market].dolaIndex; ICurvePool pool = markets[market].pool; DOLA.approve(address(pool), amount); // Support for 2 and 3 coins pools if (markets[market].length == 2) { uint256[2] memory amounts; amounts[dolaIndex] = amount; return pool.add_liquidity(amounts, minMint, recipient); } else if (markets[market].length == 3) { uint256[3] memory amounts; amounts[dolaIndex] = amount; return pool.add_liquidity(amounts, minMint, recipient); } else revert NotImplemented(); } function _removeLiquidity( ICurvePool pool, uint256 amount, uint128 dolaIndex, uint256 minOut, address recipient ) internal returns (uint256 dolaAmount) { dolaAmount = pool.remove_liquidity_one_coin( amount, int128(dolaIndex), minOut, recipient ); } function _reimbourseSharesLeft( IYearnVaultV2 vault, address recipient ) internal { uint256 sharesLeft = vault.balanceOf(address(this)); if (sharesLeft > 0) IERC20(address(vault)).safeTransfer(recipient, sharesLeft); } function _revertIfMarketNotSet(address market) internal view { if (address(markets[market].pool) == address(0)) revert MarketNotSet(market); } /** * @notice Set the market address and its associated Curve Pool and dola Index. * @dev Only callable by the governance. * @param marketAddress The address of the market. * @param dolaIndex Dola index in the coins array for Curve Pools. * @param poolAddress The address of the curve pool with DOLA. */ function setMarket( address marketAddress, address poolAddress, uint128 dolaIndex, uint128 length, address vaultAddress ) external onlyGov { markets[marketAddress] = Pool({ pool: ICurvePool(poolAddress), dolaIndex: dolaIndex, length: length, vault: IYearnVaultV2(vaultAddress) }); emit MarketSet(marketAddress, dolaIndex, poolAddress, vaultAddress); } /** * @notice Remove the market. * @dev Only callable by the governance or the guardian. * @param market The address of the market to be removed. */ function removeMarket(address market) external onlyGuardianOrGov { delete markets[market]; emit MarketRemoved(market); } }
pragma solidity ^0.8.13; import {IBorrowController, IEscrow, IOracle} from "src/Market.sol"; interface IMarket { function borrow(uint borrowAmount) external; function borrowOnBehalf( address msgSender, uint dolaAmount, uint deadline, uint8 v, bytes32 r, bytes32 s ) external; function withdraw(uint amount) external; function withdrawMax() external; function withdrawOnBehalf( address msgSender, uint amount, uint deadline, uint8 v, bytes32 r, bytes32 s ) external; function deposit(uint amount) external; function deposit(address msgSender, uint collateralAmount) external; function depositAndBorrow( uint collateralAmount, uint borrowAmount ) external; function repay(address msgSender, uint amount) external; function liquidate(address borrower, uint liquidationAmount) external; function forceReplenish(address borrower, uint deficitBefore) external; function collateral() external returns (address); function debts(address user) external returns (uint); function recall(uint amount) external; function invalidateNonce() external; function pauseBorrows(bool paused) external; function setBorrowController(IBorrowController borrowController) external; function escrows(address user) external view returns (IEscrow); function predictEscrow(address user) external view returns (IEscrow); function getCollateralValue(address user) external view returns (uint); function getWithdrawalLimit(address user) external view returns (uint); function getCreditLimit(address user) external view returns (uint); function lender() external view returns (address); function borrowController() external view returns (address); function escrowImplementation() external view returns (address); function totalDebt() external view returns (uint); function borrowPaused() external view returns (bool); function replenishmentIncentiveBps() external view returns (uint); function liquidationIncentiveBps() external view returns (uint); function collateralFactorBps() external view returns (uint); function setCollateralFactorBps(uint cfBps) external; function setOracle(IOracle oracle) external; function setGov(address newGov) external; function setLender(address newLender) external; function setPauseGuardian(address newPauseGuardian) external; function setReplenismentIncentiveBps(uint riBps) external; function setLiquidationIncentiveBps(uint liBps) external; function setLiquidationFactorBps(uint lfBps) external; function setLiquidationFeeBps(uint lfeeBps) external; function liquidationFeeBps() external view returns (uint); function DOMAIN_SEPARATOR() external view returns (uint); function oracle() external view returns (address); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; import {IERC20} from "lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol"; import {SafeERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; import {Governable} from "src/util/Governable.sol"; /** * @title Sweepable and Governable contracts * @notice This contract can be used to sweep tokens to the gov address and add governalble functionality */ contract Sweepable is Governable { using SafeERC20 for IERC20; constructor(address _gov, address _guardian) Governable(_gov, _guardian) {} /** * @notice Sweeps the specified token to the gov address * @dev Only callable by gov * @param token The address of the token to be swept */ function sweep(address token) external onlyGov { IERC20(token).safeTransfer(gov, IERC20(token).balanceOf(address(this))); } }
//SPDX-License-Identifier: None pragma solidity ^0.8.0; interface IMultiMarketTransformHelper { struct Permit { uint256 deadline; uint8 v; bytes32 r; bytes32 s; } function transformToCollateral( uint256 amount, bytes calldata data ) external returns (uint256 collateralAmount); function transformToCollateral( uint256 amount, address recipient, bytes calldata data ) external returns (uint256 collateralAmount); function transformToCollateralAndDeposit( uint256 amount, address recipient, bytes calldata data ) external returns (uint256 collateralAmount); function transformFromCollateral( uint256 amount, bytes calldata data ) external returns (uint256); function transformFromCollateral( uint256 amount, address recipient, bytes calldata data ) external returns (uint256); function withdrawAndTransformFromCollateral( uint256 amount, address recipient, Permit calldata permit, bytes calldata data ) external returns (uint256 underlyingAmount); }
pragma solidity ^0.8.13; interface ICurvePool { function price_oracle(uint256 k) external view returns (uint256); function get_virtual_price() external view returns (uint256); function price_oracle() external view returns (uint256); function add_liquidity( uint256[2] memory _amounts, uint256 _min_mint_amount, address _receiver ) external returns (uint256); function add_liquidity( uint256[2] memory _amounts, uint256 _min_mint_amount ) external returns (uint256); function add_liquidity( uint256[3] memory _amounts, uint256 _min_mint_amount, address _receiver ) external returns (uint256); function add_liquidity( uint256[3] memory _amounts, uint256 _min_mint_amount ) external returns (uint256); function remove_liquidity_one_coin( uint256 _burn_amount, int128 i, uint256 _min_received, address _receiver ) external returns (uint256); function coins(uint index) external view returns (address); function exchange( uint i, uint j, uint dx, uint min_dy, bool use_eth, address receiver ) external payable returns (uint); function calc_token_amount( uint256[2] memory _amounts, bool _is_deposit ) external view returns (uint256); function calc_withdraw_one_coin( uint256 _burn_amount, int128 i ) external view returns (uint256); }
pragma solidity ^0.8.13; interface IYearnVaultV2 { function deposit( uint256 amount, address recipient ) external returns (uint256); function withdraw( uint256 shares, address recipient ) external returns (uint256); function withdraw( uint256 shares, address recipient, uint256 maxLoss ) external returns (uint256); function withdraw(uint256 amount) external returns (uint256); function totalSupply() external view returns (uint256); function pricePerShare() external view returns (uint256); function totalAssets() external view returns (uint256); function lastReport() external view returns (uint256); function lockedProfitDegradation() external view returns (uint256); function lockedProfit() external view returns (uint256); function balanceOf(address account) external view returns (uint256); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; import "src/interfaces/IERC20.sol"; // Caution. We assume all failed transfers cause reverts and ignore the returned bool. interface IOracle { function getPrice(address,uint) external returns (uint); function viewPrice(address,uint) external view returns (uint); } interface IEscrow { function initialize(IERC20 _token, address beneficiary) external; function onDeposit() external; function pay(address recipient, uint amount) external; function balance() external view returns (uint); } interface IDolaBorrowingRights { function onBorrow(address user, uint additionalDebt) external; function onRepay(address user, uint repaidDebt) external; function onForceReplenish(address user, address replenisher, uint amount, uint replenisherReward) external; function balanceOf(address user) external view returns (uint); function deficitOf(address user) external view returns (uint); function replenishmentPriceBps() external view returns (uint); } interface IBorrowController { function borrowAllowed(address msgSender, address borrower, uint amount) external returns (bool); function onRepay(uint amount) external; } contract Market { address public gov; address public lender; address public pauseGuardian; address public immutable escrowImplementation; IDolaBorrowingRights public immutable dbr; IBorrowController public borrowController; IERC20 public immutable dola = IERC20(0x865377367054516e17014CcdED1e7d814EDC9ce4); IERC20 public immutable collateral; IOracle public oracle; uint public collateralFactorBps; uint public replenishmentIncentiveBps; uint public liquidationIncentiveBps; uint public liquidationFeeBps; uint public liquidationFactorBps = 5000; // 50% by default bool immutable callOnDepositCallback; bool public borrowPaused; uint public totalDebt; uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping (address => IEscrow) public escrows; // user => escrow mapping (address => uint) public debts; // user => debt mapping(address => uint256) public nonces; // user => nonce constructor ( address _gov, address _lender, address _pauseGuardian, address _escrowImplementation, IDolaBorrowingRights _dbr, IERC20 _collateral, IOracle _oracle, uint _collateralFactorBps, uint _replenishmentIncentiveBps, uint _liquidationIncentiveBps, bool _callOnDepositCallback ) { require(_collateralFactorBps < 10000, "Invalid collateral factor"); require(_liquidationIncentiveBps > 0 && _liquidationIncentiveBps < 10000, "Invalid liquidation incentive"); require(_replenishmentIncentiveBps < 10000, "Replenishment incentive must be less than 100%"); gov = _gov; lender = _lender; pauseGuardian = _pauseGuardian; escrowImplementation = _escrowImplementation; dbr = _dbr; collateral = _collateral; oracle = _oracle; collateralFactorBps = _collateralFactorBps; replenishmentIncentiveBps = _replenishmentIncentiveBps; liquidationIncentiveBps = _liquidationIncentiveBps; callOnDepositCallback = _callOnDepositCallback; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); if(collateralFactorBps > 0){ uint unsafeLiquidationIncentive = (10000 - collateralFactorBps) * (liquidationFeeBps + 10000) / collateralFactorBps; require(liquidationIncentiveBps < unsafeLiquidationIncentive, "Liquidation param allow profitable self liquidation"); } } modifier onlyGov { require(msg.sender == gov, "Only gov can call this function"); _; } modifier liquidationParamChecker { _; if(collateralFactorBps > 0){ uint unsafeLiquidationIncentive = (10000 - collateralFactorBps) * (liquidationFeeBps + 10000) / collateralFactorBps; require(liquidationIncentiveBps < unsafeLiquidationIncentive, "New liquidation param allow profitable self liquidation"); } } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes("DBR MARKET")), keccak256("1"), block.chainid, address(this) ) ); } /** @notice sets the oracle to a new oracle. Only callable by governance. @param _oracle The new oracle conforming to the IOracle interface. */ function setOracle(IOracle _oracle) public onlyGov { oracle = _oracle; } /** @notice sets the borrow controller to a new borrow controller. Only callable by governance. @param _borrowController The new borrow controller conforming to the IBorrowController interface. */ function setBorrowController(IBorrowController _borrowController) public onlyGov { borrowController = _borrowController; } /** @notice sets the address of governance. Only callable by governance. @param _gov Address of the new governance. */ function setGov(address _gov) public onlyGov { gov = _gov; } /** @notice sets the lender to a new lender. The lender is allowed to recall dola from the contract. Only callable by governance. @param _lender Address of the new lender. */ function setLender(address _lender) public onlyGov { lender = _lender; } /** @notice sets the pause guardian. The pause guardian can pause borrowing. Only callable by governance. @param _pauseGuardian Address of the new pauseGuardian. */ function setPauseGuardian(address _pauseGuardian) public onlyGov { pauseGuardian = _pauseGuardian; } /** @notice sets the Collateral Factor requirement of the market as measured in basis points. 1 = 0.01%. Only callable by governance. @dev Collateral factor mus be set below 100% @param _collateralFactorBps The new collateral factor as measured in basis points. */ function setCollateralFactorBps(uint _collateralFactorBps) public onlyGov liquidationParamChecker { require(_collateralFactorBps < 10000, "Invalid collateral factor"); collateralFactorBps = _collateralFactorBps; } /** @notice sets the Liquidation Factor of the market as denoted in basis points. The liquidation Factor denotes the maximum amount of debt that can be liquidated in basis points. At 5000, 50% of of a borrower's underwater debt can be liquidated. Only callable by governance. @dev Must be set between 1 and 10000. @param _liquidationFactorBps The new liquidation factor in basis points. 1 = 0.01%/ */ function setLiquidationFactorBps(uint _liquidationFactorBps) public onlyGov { require(_liquidationFactorBps > 0 && _liquidationFactorBps <= 10000, "Invalid liquidation factor"); liquidationFactorBps = _liquidationFactorBps; } /** @notice sets the Replenishment Incentive of the market as denoted in basis points. The Replenishment Incentive is the percentage paid out to replenishers on a successful forceReplenish call, denoted in basis points. @dev Must be set between 1 and 10000. @param _replenishmentIncentiveBps The new replenishment incentive set in basis points. 1 = 0.01% */ function setReplenismentIncentiveBps(uint _replenishmentIncentiveBps) public onlyGov { require(_replenishmentIncentiveBps > 0 && _replenishmentIncentiveBps < 10000, "Invalid replenishment incentive"); replenishmentIncentiveBps = _replenishmentIncentiveBps; } /** @notice sets the Liquidation Incentive of the market as denoted in basis points. The Liquidation Incentive is the percentage paid out to liquidators of a borrower's debt when successfully liquidated. @dev Must be set between 0 and 10000 - liquidation fee. @param _liquidationIncentiveBps The new liqudation incentive set in basis points. 1 = 0.01% */ function setLiquidationIncentiveBps(uint _liquidationIncentiveBps) public onlyGov liquidationParamChecker { require(_liquidationIncentiveBps > 0 && _liquidationIncentiveBps + liquidationFeeBps < 10000, "Invalid liquidation incentive"); liquidationIncentiveBps = _liquidationIncentiveBps; } /** @notice sets the Liquidation Fee of the market as denoted in basis points. The Liquidation Fee is the percentage paid out to governance of a borrower's debt when successfully liquidated. @dev Must be set between 0 and 10000 - liquidation factor. @param _liquidationFeeBps The new liquidation fee set in basis points. 1 = 0.01% */ function setLiquidationFeeBps(uint _liquidationFeeBps) public onlyGov liquidationParamChecker { require(_liquidationFeeBps > 0 && _liquidationFeeBps + liquidationIncentiveBps < 10000, "Invalid liquidation fee"); liquidationFeeBps = _liquidationFeeBps; } /** @notice Recalls amount of DOLA to the lender. @param amount The amount od DOLA to recall to the the lender. */ function recall(uint amount) public { require(msg.sender == lender, "Only lender can recall"); dola.transfer(msg.sender, amount); } /** @notice Pauses or unpauses borrowing for the market. Only gov can unpause a market, while gov and pauseGuardian can pause it. @param _value Boolean representing the state pause state of borrows. true = paused, false = unpaused. */ function pauseBorrows(bool _value) public { if(_value) { require(msg.sender == pauseGuardian || msg.sender == gov, "Only pause guardian or governance can pause"); } else { require(msg.sender == gov, "Only governance can unpause"); } borrowPaused = _value; } /** @notice Internal function for creating an escrow for users to deposit collateral in. @dev Uses create2 and minimal proxies to create the escrow at a deterministic address @param user The address of the user to create an escrow for. */ function createEscrow(address user) internal returns (IEscrow instance) { address implementation = escrowImplementation; /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) mstore(add(ptr, 0x14), shl(0x60, implementation)) mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) instance := create2(0, ptr, 0x37, user) } require(instance != IEscrow(address(0)), "ERC1167: create2 failed"); emit CreateEscrow(user, address(instance)); } /** @notice Internal function for getting the escrow of a user. @dev If the escrow doesn't exist, an escrow contract is deployed. @param user The address of the user owning the escrow. */ function getEscrow(address user) internal returns (IEscrow) { if(escrows[user] != IEscrow(address(0))) return escrows[user]; IEscrow escrow = createEscrow(user); escrow.initialize(collateral, user); escrows[user] = escrow; return escrow; } /** @notice Deposit amount of collateral into escrow @dev Will deposit the amount into the escrow contract. @param amount Amount of collateral token to deposit. */ function deposit(uint amount) public { deposit(msg.sender, amount); } /** @notice Deposit and borrow in a single transaction. @param amountDeposit Amount of collateral token to deposit into escrow. @param amountBorrow Amount of DOLA to borrow. */ function depositAndBorrow(uint amountDeposit, uint amountBorrow) public { deposit(amountDeposit); borrow(amountBorrow); } /** @notice Deposit amount of collateral into escrow on behalf of msg.sender @dev Will deposit the amount into the escrow contract. @param user User to deposit on behalf of. @param amount Amount of collateral token to deposit. */ function deposit(address user, uint amount) public { IEscrow escrow = getEscrow(user); collateral.transferFrom(msg.sender, address(escrow), amount); if(callOnDepositCallback) { escrow.onDeposit(); } emit Deposit(user, amount); } /** @notice View function for predicting the deterministic escrow address of a user. @dev Only use deposit() function for deposits and NOT the predicted escrow address unless you know what you're doing @param user Address of the user owning the escrow. */ function predictEscrow(address user) public view returns (IEscrow predicted) { address implementation = escrowImplementation; address deployer = address(this); /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) mstore(add(ptr, 0x14), shl(0x60, implementation)) mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000) mstore(add(ptr, 0x38), shl(0x60, deployer)) mstore(add(ptr, 0x4c), user) mstore(add(ptr, 0x6c), keccak256(ptr, 0x37)) predicted := keccak256(add(ptr, 0x37), 0x55) } } /** @notice View function for getting the dollar value of the user's collateral in escrow for the market. @param user Address of the user. */ function getCollateralValue(address user) public view returns (uint) { IEscrow escrow = predictEscrow(user); uint collateralBalance = escrow.balance(); return collateralBalance * oracle.viewPrice(address(collateral), collateralFactorBps) / 1 ether; } /** @notice Internal function for getting the dollar value of the user's collateral in escrow for the market. @dev Updates the lowest price comparisons of the pessimistic oracle @param user Address of the user. */ function getCollateralValueInternal(address user) internal returns (uint) { IEscrow escrow = predictEscrow(user); uint collateralBalance = escrow.balance(); return collateralBalance * oracle.getPrice(address(collateral), collateralFactorBps) / 1 ether; } /** @notice View function for getting the credit limit of a user. @dev To calculate the available credit, subtract user debt from credit limit. @param user Address of the user. */ function getCreditLimit(address user) public view returns (uint) { uint collateralValue = getCollateralValue(user); return collateralValue * collateralFactorBps / 10000; } /** @notice Internal function for getting the credit limit of a user. @dev To calculate the available credit, subtract user debt from credit limit. Updates the pessimistic oracle. @param user Address of the user. */ function getCreditLimitInternal(address user) internal returns (uint) { uint collateralValue = getCollateralValueInternal(user); return collateralValue * collateralFactorBps / 10000; } /** @notice Internal function for getting the withdrawal limit of a user. The withdrawal limit is how much collateral a user can withdraw before their loan would be underwater. Updates the pessimistic oracle. @param user Address of the user. */ function getWithdrawalLimitInternal(address user) internal returns (uint) { IEscrow escrow = predictEscrow(user); uint collateralBalance = escrow.balance(); if(collateralBalance == 0) return 0; uint debt = debts[user]; if(debt == 0) return collateralBalance; if(collateralFactorBps == 0) return 0; uint minimumCollateral = debt * 1 ether / oracle.getPrice(address(collateral), collateralFactorBps) * 10000 / collateralFactorBps; if(collateralBalance <= minimumCollateral) return 0; return collateralBalance - minimumCollateral; } /** @notice View function for getting the withdrawal limit of a user. The withdrawal limit is how much collateral a user can withdraw before their loan would be underwater. @param user Address of the user. */ function getWithdrawalLimit(address user) public view returns (uint) { IEscrow escrow = predictEscrow(user); uint collateralBalance = escrow.balance(); if(collateralBalance == 0) return 0; uint debt = debts[user]; if(debt == 0) return collateralBalance; if(collateralFactorBps == 0) return 0; uint minimumCollateral = debt * 1 ether / oracle.viewPrice(address(collateral), collateralFactorBps) * 10000 / collateralFactorBps; if(collateralBalance <= minimumCollateral) return 0; return collateralBalance - minimumCollateral; } /** @notice Internal function for borrowing DOLA against collateral. @dev This internal function is shared between the borrow and borrowOnBehalf function @param borrower The address of the borrower that debt will be accrued to. @param to The address that will receive the borrowed DOLA @param amount The amount of DOLA to be borrowed */ function borrowInternal(address borrower, address to, uint amount) internal { require(!borrowPaused, "Borrowing is paused"); if(borrowController != IBorrowController(address(0))) { require(borrowController.borrowAllowed(msg.sender, borrower, amount), "Denied by borrow controller"); } uint credit = getCreditLimitInternal(borrower); debts[borrower] += amount; require(credit >= debts[borrower], "Exceeded credit limit"); totalDebt += amount; dbr.onBorrow(borrower, amount); dola.transfer(to, amount); emit Borrow(borrower, amount); } /** @notice Function for borrowing DOLA. @dev Will borrow to msg.sender @param amount The amount of DOLA to be borrowed. */ function borrow(uint amount) public { borrowInternal(msg.sender, msg.sender, amount); } /** @notice Function for using a signed message to borrow on behalf of an address owning an escrow with collateral. @dev Signed messaged can be invalidated by incrementing the nonce. Will always borrow to the msg.sender. @param from The address of the user being borrowed from @param amount The amount to be borrowed @param deadline Timestamp after which the signed message will be invalid @param v The v param of the ECDSA signature @param r The r param of the ECDSA signature @param s The s param of the ECDSA signature */ function borrowOnBehalf(address from, uint amount, uint deadline, uint8 v, bytes32 r, bytes32 s) public { require(deadline >= block.timestamp, "DEADLINE_EXPIRED"); unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" ), msg.sender, from, amount, nonces[from]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == from, "INVALID_SIGNER"); borrowInternal(from, msg.sender, amount); } } /** @notice Internal function for withdrawing from the escrow @dev The internal function is shared by the withdraw function and withdrawOnBehalf function @param from The address owning the escrow to withdraw from. @param to The address receiving the tokens @param amount The amount being withdrawn. */ function withdrawInternal(address from, address to, uint amount) internal { uint limit = getWithdrawalLimitInternal(from); require(limit >= amount, "Insufficient withdrawal limit"); require(dbr.deficitOf(from) == 0, "Can't withdraw with DBR deficit"); IEscrow escrow = getEscrow(from); escrow.pay(to, amount); emit Withdraw(from, to, amount); } /** @notice Function for withdrawing to msg.sender. @param amount Amount to withdraw. */ function withdraw(uint amount) public { withdrawInternal(msg.sender, msg.sender, amount); } /** @notice Function for withdrawing maximum allowed to msg.sender. @dev Useful for use with escrows that continously compound tokens, so there won't be dust amounts left @dev Dangerous to use when the user has any amount of debt! */ function withdrawMax() public { withdrawInternal(msg.sender, msg.sender, getWithdrawalLimitInternal(msg.sender)); } /** @notice Function for using a signed message to withdraw on behalf of an address owning an escrow with collateral. @dev Signed messaged can be invalidated by incrementing the nonce. Will always withdraw to the msg.sender. @param from The address of the user owning the escrow being withdrawn from @param amount The amount to be withdrawn @param deadline Timestamp after which the signed message will be invalid @param v The v param of the ECDSA signature @param r The r param of the ECDSA signature @param s The s param of the ECDSA signature */ function withdrawOnBehalf(address from, uint amount, uint deadline, uint8 v, bytes32 r, bytes32 s) public { require(deadline >= block.timestamp, "DEADLINE_EXPIRED"); unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" ), msg.sender, from, amount, nonces[from]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == from, "INVALID_SIGNER"); withdrawInternal(from, msg.sender, amount); } } /** @notice Function for using a signed message to withdraw on behalf of an address owning an escrow with collateral. @dev Signed messaged can be invalidated by incrementing the nonce. Will always withdraw to the msg.sender. @dev Useful for use with escrows that continously compound tokens, so there won't be dust amounts left @dev Dangerous to use when the user has any amount of debt! @param from The address of the user owning the escrow being withdrawn from @param deadline Timestamp after which the signed message will be invalid @param v The v param of the ECDSA signature @param r The r param of the ECDSA signature @param s The s param of the ECDSA signature */ function withdrawMaxOnBehalf(address from, uint deadline, uint8 v, bytes32 r, bytes32 s) public { require(deadline >= block.timestamp, "DEADLINE_EXPIRED"); unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "WithdrawMaxOnBehalf(address caller,address from,uint256 nonce,uint256 deadline)" ), msg.sender, from, nonces[from]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == from, "INVALID_SIGNER"); withdrawInternal(from, msg.sender, getWithdrawalLimitInternal(from)); } } /** @notice Function for incrementing the nonce of the msg.sender, making their latest signed message unusable. */ function invalidateNonce() public { nonces[msg.sender]++; } /** @notice Function for repaying debt on behalf of user. Debt must be repaid in DOLA. @dev If the user has a DBR deficit, they risk initial debt being accrued by forced replenishments. @param user Address of the user whose debt is being repaid @param amount DOLA amount to be repaid. If set to max uint debt will be repaid in full. */ function repay(address user, uint amount) public { uint debt = debts[user]; if(amount == type(uint).max){ amount = debt; } require(debt >= amount, "Repayment greater than debt"); debts[user] -= amount; totalDebt -= amount; dbr.onRepay(user, amount); if(address(borrowController) != address(0)){ borrowController.onRepay(amount); } dola.transferFrom(msg.sender, address(this), amount); emit Repay(user, msg.sender, amount); } /** @notice Bundles repayment and withdrawal into a single function call. @param repayAmount Amount of DOLA to be repaid @param withdrawAmount Amount of underlying to be withdrawn from the escrow */ function repayAndWithdraw(uint repayAmount, uint withdrawAmount) public { repay(msg.sender, repayAmount); withdraw(withdrawAmount); } /** @notice Function for forcing a user to replenish their DBR deficit at a pre-determined price. The replenishment will accrue additional DOLA debt. On a successful call, the caller will be paid a replenishment incentive. @dev The function will only top the user back up to 0, meaning that the user will have a DBR deficit again in the next block. @param user The address of the user being forced to replenish DBR @param amount The amount of DBR the user will be replenished. */ function forceReplenish(address user, uint amount) public { uint deficit = dbr.deficitOf(user); require(deficit > 0, "No DBR deficit"); require(deficit >= amount, "Amount > deficit"); uint replenishmentCost = amount * dbr.replenishmentPriceBps() / 10000; uint replenisherReward = replenishmentCost * replenishmentIncentiveBps / 10000; debts[user] += replenishmentCost; uint collateralValue = getCollateralValueInternal(user) * (10000 - liquidationIncentiveBps - liquidationFeeBps) / 10000; require(collateralValue >= debts[user], "Exceeded collateral value"); totalDebt += replenishmentCost; dbr.onForceReplenish(user, msg.sender, amount, replenisherReward); dola.transfer(msg.sender, replenisherReward); } /** @notice Function for forcing a user to replenish all of their DBR deficit at a pre-determined price. The replenishment will accrue additional DOLA debt. On a successful call, the caller will be paid a replenishment incentive. @dev The function will only top the user back up to 0, meaning that the user will have a DBR deficit again in the next block. @param user The address of the user being forced to replenish DBR */ function forceReplenishAll(address user) public { uint deficit = dbr.deficitOf(user); forceReplenish(user, deficit); } /** @notice Function for liquidating a user's under water debt. Debt is under water when the value of a user's debt is above their collateral factor. @param user The user to be liquidated @param repaidDebt Th amount of user user debt to liquidate. */ function liquidate(address user, uint repaidDebt) public { require(repaidDebt > 0, "Must repay positive debt"); uint debt = debts[user]; require(getCreditLimitInternal(user) < debt, "User debt is healthy"); require(repaidDebt <= debt * liquidationFactorBps / 10000, "Exceeded liquidation factor"); uint price = oracle.getPrice(address(collateral), collateralFactorBps); uint liquidatorReward = repaidDebt * 1 ether / price; liquidatorReward += liquidatorReward * liquidationIncentiveBps / 10000; debts[user] -= repaidDebt; totalDebt -= repaidDebt; dbr.onRepay(user, repaidDebt); if(address(borrowController) != address(0)){ borrowController.onRepay(repaidDebt); } dola.transferFrom(msg.sender, address(this), repaidDebt); IEscrow escrow = predictEscrow(user); escrow.pay(msg.sender, liquidatorReward); if(liquidationFeeBps > 0) { uint liquidationFee = repaidDebt * 1 ether / price * liquidationFeeBps / 10000; uint balance = escrow.balance(); if(balance >= liquidationFee) { escrow.pay(gov, liquidationFee); } else if(balance > 0) { escrow.pay(gov, balance); } } emit Liquidate(user, msg.sender, repaidDebt, liquidatorReward); } event Deposit(address indexed account, uint amount); event Borrow(address indexed account, uint amount); event Withdraw(address indexed account, address indexed to, uint amount); event Repay(address indexed account, address indexed repayer, uint amount); event Liquidate(address indexed account, address indexed liquidator, uint repaidDebt, uint liquidatorReward); event CreateEscrow(address indexed user, address escrow); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../token/ERC20/IERC20.sol";
// 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: UNLICENSED pragma solidity ^0.8.13; /** * @title Governable * @notice This contract is add Governable functionality to the contract */ contract Governable { error NotGov(); error NotPendingGov(); error NotGuardianOrGov(); address public gov; address public pendingGov; address public guardian; event NewGov(address gov); event NewPendingGov(address pendingGov); event NewGuardian(address guardian); /** @dev Constructor @param _gov The address of Inverse Finance governance @param _guardian The address of the guardian **/ constructor(address _gov, address _guardian) { gov = _gov; guardian = _guardian; } modifier onlyGov() { if (msg.sender != gov) revert NotGov(); _; } modifier onlyGuardianOrGov() { if (msg.sender != guardian || msg.sender != gov) revert NotGuardianOrGov(); _; } /** * @notice Sets the pendingGov, which can claim gov role. * @dev Only callable by gov * @param _pendingGov The address of the pendingGov */ function setPendingGov(address _pendingGov) external onlyGov { pendingGov = _pendingGov; emit NewPendingGov(_pendingGov); } /** * @notice Claims the gov role * @dev Only callable by pendingGov */ function claimPendingGov() external { if (msg.sender != pendingGov) revert NotPendingGov(); gov = pendingGov; pendingGov = address(0); emit NewGov(gov); } /** * @notice Sets the guardian role * @dev Only callable by gov * @param _guardian The address of the guardian */ function setGuardian(address _guardian) external onlyGov { guardian = _guardian; emit NewGuardian(_guardian); } }
pragma solidity ^0.8.13; interface IERC20 { function approve(address,uint) external; function transfer(address,uint) external returns (bool); function transferFrom(address,address,uint) external returns (bool); function balanceOf(address) external view returns (uint); function allowance(address from, address to) external view returns (uint); } interface IMintable is IERC20 { function mint(address,uint) external; function burn(uint) external; function addMinter(address minter) external; } interface IDelegateableERC20 is IERC20 { function delegate(address delegatee) external; function delegates(address delegator) external view returns (address delegatee); }
// 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/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) (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(); } } }
{ "remappings": [ "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "solmate/=lib/solmate/src/" ], "optimizer": { "enabled": true, "runs": 10000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "shanghai", "viaIR": false, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_gov","type":"address"},{"internalType":"address","name":"_guardian","type":"address"},{"internalType":"address","name":"_dola","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":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InsufficientLP","type":"error"},{"inputs":[],"name":"InsufficientShares","type":"error"},{"inputs":[{"internalType":"address","name":"market","type":"address"}],"name":"MarketNotSet","type":"error"},{"inputs":[],"name":"NotGov","type":"error"},{"inputs":[],"name":"NotGuardianOrGov","type":"error"},{"inputs":[],"name":"NotImplemented","type":"error"},{"inputs":[],"name":"NotPendingGov","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"market","type":"address"}],"name":"MarketRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"market","type":"address"},{"indexed":false,"internalType":"uint128","name":"dolaIndex","type":"uint128"},{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":true,"internalType":"address","name":"yearnVault","type":"address"}],"name":"MarketSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"gov","type":"address"}],"name":"NewGov","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"guardian","type":"address"}],"name":"NewGuardian","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pendingGov","type":"address"}],"name":"NewPendingGov","type":"event"},{"inputs":[],"name":"DOLA","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimPendingGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gov","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"guardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"markets","outputs":[{"internalType":"contract ICurvePool","name":"pool","type":"address"},{"internalType":"uint128","name":"dolaIndex","type":"uint128"},{"internalType":"uint128","name":"length","type":"uint128"},{"internalType":"contract IYearnVaultV2","name":"vault","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingGov","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"market","type":"address"}],"name":"removeMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_guardian","type":"address"}],"name":"setGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"marketAddress","type":"address"},{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"uint128","name":"dolaIndex","type":"uint128"},{"internalType":"uint128","name":"length","type":"uint128"},{"internalType":"address","name":"vaultAddress","type":"address"}],"name":"setMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_pendingGov","type":"address"}],"name":"setPendingGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"sweep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"transformFromCollateral","outputs":[{"internalType":"uint256","name":"dolaAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"transformFromCollateral","outputs":[{"internalType":"uint256","name":"dolaAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"transformToCollateral","outputs":[{"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"transformToCollateral","outputs":[{"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"transformToCollateralAndDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"components":[{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct IMultiMarketTransformHelper.Permit","name":"permit","type":"tuple"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"withdrawAndTransformFromCollateral","outputs":[{"internalType":"uint256","name":"dolaAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60a060405234801562000010575f80fd5b5060405162001cc938038062001cc9833981016040819052620000339162000088565b5f80546001600160a01b03199081166001600160a01b0395861617909155600280549091169284169290921790915516608052620000cf565b80516001600160a01b038116811462000083575f80fd5b919050565b5f805f606084860312156200009b575f80fd5b620000a6846200006c565b9250620000b6602085016200006c565b9150620000c6604085016200006c565b90509250925092565b608051611bd3620000f65f395f81816102b30152818161115a01526111f20152611bd35ff3fe608060405234801561000f575f80fd5b5060043610610115575f3560e01c80637062b6cf116100ad578063a04b9ccb1161007d578063db91323611610063578063db913236146102fb578063efdf0bb01461030e578063f0c9e46514610321575f80fd5b8063a04b9ccb146102d5578063a8149fdd146102e8575f80fd5b80637062b6cf146101dd5780638a0dac4a146101f05780638e8f294b1461020357806392c592d0146102ae575f80fd5b80633585e70f116100e85780633585e70f14610191578063452a9320146101a45780634ee54921146101b757806356fc300c146101ca575f80fd5b806301681a621461011957806312d43a511461012e578063252408101461015d5780632cfa26df14610170575b5f80fd5b61012c610127366004611814565b610329565b005b5f54610140906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b600154610140906001600160a01b031681565b61018361017e366004611874565b61040a565b604051908152602001610154565b61012c61019f3660046118e0565b61041f565b600254610140906001600160a01b031681565b6101836101c5366004611874565b61055f565b6101836101d8366004611949565b61056c565b6101836101eb366004611949565b610702565b61012c6101fe366004611814565b6109ba565b610267610211366004611814565b60036020525f90815260409020805460018201546002909201546001600160a01b03918216926fffffffffffffffffffffffffffffffff8082169370010000000000000000000000000000000090920416911684565b60405161015494939291906001600160a01b0394851681526fffffffffffffffffffffffffffffffff93841660208201529190921660408201529116606082015260800190565b6101407f000000000000000000000000000000000000000000000000000000000000000081565b6101836102e33660046119a1565b610a6a565b6101836102f6366004611949565b610cfb565b61012c610309366004611814565b610e3b565b61012c61031c366004611814565b610f11565b61012c610fba565b5f546001600160a01b0316331461036c576040517fb577c1f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f546040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152610407916001600160a01b0390811691908416906370a0823190602401602060405180830381865afa1580156103d2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103f69190611a36565b6001600160a01b0384169190611073565b50565b5f6104178433858561056c565b949350505050565b5f546001600160a01b03163314610462576040517fb577c1f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516080810182526001600160a01b038681168083526fffffffffffffffffffffffffffffffff8781166020808601828152898416878901908152898716606089018181528f89165f818152600387528c90209a518b54908b167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216178c5594519351881670010000000000000000000000000000000002939097169290921760018a0155905160029098018054989097169790911696909617909455945194855292939092917f57fbc79da3742e8259f8c34c2ed0447c05a5fe4e7b1657d29b74128d03badbe4910160405180910390a45050505050565b5f61041784338585610cfb565b5f808061057b84860186611a4d565b91509150610588826110ec565b6001600160a01b038083165f908152600360205260409020600201541680156106ea575f6105b8848a853061114c565b6001600160a01b038581165f90815260036020526040908190205490517f095ea7b3000000000000000000000000000000000000000000000000000000008152858316600482015260248101849052929350169063095ea7b3906044016020604051808303815f875af1158015610631573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106559190611a77565b506040517f6e553f65000000000000000000000000000000000000000000000000000000008152600481018290526001600160a01b038981166024830152831690636e553f65906044016020604051808303815f875af11580156106bb573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106df9190611a36565b945050505050610417565b6106f68389848a61114c565b98975050505050505050565b5f8061071083850185611a4d565b50905061071c816110ec565b5f6107298730878761056c565b6001600160a01b038084165f908152600360205260408120600201549293509116908082156107db57506040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015282906001600160a01b038216906370a0823190602401602060405180830381865afa1580156107b0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107d49190611a36565b9150610872565b506001600160a01b038481165f90815260036020526040908190205490517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015291169081906370a0823190602401602060405180830381865afa15801561084b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061086f9190611a36565b91505b818411156108ac576040517f3999656700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b0386811660048301526024820184905282169063095ea7b3906044016020604051808303815f875af1158015610911573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109359190611a77565b506040517f47e7ef240000000000000000000000000000000000000000000000000000000081526001600160a01b038a81166004830152602482018490528616906347e7ef24906044015f604051808303815f87803b158015610996575f80fd5b505af11580156109a8573d5f803e3d5ffd5b50939c9b505050505050505050505050565b5f546001600160a01b031633146109fd576040517fb577c1f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527fb6182387b7ea948602a7e04e662a27ce251dc3dd014eacaed10dce36b41bf1a5906020015b60405180910390a150565b5f8080610a7984860186611a4d565b91509150610a86826110ec565b6001600160a01b038216633525f591338a8935610aa960408c0160208d01611a96565b604080517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b1681526001600160a01b0390951660048601526024850193909352604484019190915260ff1660648301528901356084820152606089013560a482015260c4015f604051808303815f87803b158015610b2a575f80fd5b505af1158015610b3c573d5f803e3d5ffd5b5050506001600160a01b038084165f90815260036020526040902080546002909101549082169250168015610bf8576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018b90526001600160a01b03821690632e1a7d4d906024016020604051808303815f875af1158015610bc8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bec9190611a36565b9950610bf8818a611459565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201528a906001600160a01b038416906370a0823190602401602060405180830381865afa158015610c55573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c799190611a36565b1015610cb1576040517f955a8bf500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0384165f90815260036020526040902060010154610ced9083908c906fffffffffffffffffffffffffffffffff16868d6114f6565b9a9950505050505050505050565b5f8080610d0a84860186611a4d565b91509150610d17826110ec565b6001600160a01b038083165f908152600360205260408120805460028201546001909201549084169391909116916fffffffffffffffffffffffffffffffff909116908215610e0757610d756001600160a01b03841633308e61159d565b6040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018c90526001600160a01b03841690632e1a7d4d906024016020604051808303815f875af1158015610dd2573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610df69190611a36565b9050610e02838b611459565b610e1f565b610e1c6001600160a01b03851633308e61159d565b50895b610e2c848284888e6114f6565b9b9a5050505050505050505050565b6002546001600160a01b031633141580610e5f57505f546001600160a01b03163314155b15610e96576040517fe6000c8500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381165f8181526003602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000908116825560018201849055600290910180549091169055517f59d7b1e52008dc342c9421dadfc773114b914a65682a4e4b53cf60a970df0d779190a250565b5f546001600160a01b03163314610f54576040517fb577c1f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527ff74ae56780e3765c0c0897ef57fb50a10a237584f419631812daf040913e1c9f90602001610a5f565b6001546001600160a01b03163314610ffe576040517f7c04d72b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180545f80546001600160a01b0383167fffffffffffffffffffffffff000000000000000000000000000000000000000091821681179092559091169091556040519081527f639717155292ce2c3e699929a8b65d14a637640f75ab5b6d165a4e735d82a4559060200160405180910390a1565b6040516001600160a01b038381166024830152604482018390526110e791859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506115dc565b505050565b6001600160a01b038181165f9081526003602052604090205416610407576040517f31589d090000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024015b60405180910390fd5b5f6111826001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633308761159d565b6001600160a01b038581165f90815260036020526040908190206001810154905491517f095ea7b300000000000000000000000000000000000000000000000000000000815291831660048301819052602483018890526fffffffffffffffffffffffffffffffff9091169290917f00000000000000000000000000000000000000000000000000000000000000009091169063095ea7b3906044016020604051808303815f875af115801561123a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061125e9190611a77565b506001600160a01b0387165f9081526003602052604090206001015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16600203611362576112af6117c4565b8681846fffffffffffffffffffffffffffffffff16600281106112d4576112d4611ab6565b60200201526040517f0c3e4b540000000000000000000000000000000000000000000000000000000081526001600160a01b03831690630c3e4b54906113229084908a908a90600401611ae3565b6020604051808303815f875af115801561133e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106f69190611a36565b6001600160a01b0387165f9081526003602081905260409091206001015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff169003611427576113b46117e2565b8681846fffffffffffffffffffffffffffffffff16600381106113d9576113d9611ab6565b60200201526040517f75b96abc0000000000000000000000000000000000000000000000000000000081526001600160a01b038316906375b96abc906113229084908a908a90600401611b2a565b6040517fd623472500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f906001600160a01b038416906370a0823190602401602060405180830381865afa1580156114b6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114da9190611a36565b905080156110e7576110e76001600160a01b0384168383611073565b6040517f081579a500000000000000000000000000000000000000000000000000000000815260048101859052600f84900b6024820152604481018390526001600160a01b0382811660648301525f919087169063081579a5906084016020604051808303815f875af115801561156f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115939190611a36565b9695505050505050565b6040516001600160a01b0384811660248301528381166044830152606482018390526115d69186918216906323b872dd906084016110a0565b50505050565b5f6115f06001600160a01b03841683611656565b905080515f141580156116145750808060200190518101906116129190611a77565b155b156110e7576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401611143565b606061166383835f61166a565b9392505050565b6060814710156116a8576040517fcd786059000000000000000000000000000000000000000000000000000000008152306004820152602401611143565b5f80856001600160a01b031684866040516116c39190611b71565b5f6040518083038185875af1925050503d805f81146116fd576040519150601f19603f3d011682016040523d82523d5f602084013e611702565b606091505b50915091506115938683836060826117225761171d82611782565b611663565b815115801561173957506001600160a01b0384163b155b1561177b576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401611143565b5080611663565b8051156117925780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052806002906020820280368337509192915050565b60405180606001604052806003906020820280368337509192915050565b6001600160a01b0381168114610407575f80fd5b5f60208284031215611824575f80fd5b813561166381611800565b5f8083601f84011261183f575f80fd5b50813567ffffffffffffffff811115611856575f80fd5b60208301915083602082850101111561186d575f80fd5b9250929050565b5f805f60408486031215611886575f80fd5b83359250602084013567ffffffffffffffff8111156118a3575f80fd5b6118af8682870161182f565b9497909650939450505050565b80356fffffffffffffffffffffffffffffffff811681146118db575f80fd5b919050565b5f805f805f60a086880312156118f4575f80fd5b85356118ff81611800565b9450602086013561190f81611800565b935061191d604087016118bc565b925061192b606087016118bc565b9150608086013561193b81611800565b809150509295509295909350565b5f805f806060858703121561195c575f80fd5b84359350602085013561196e81611800565b9250604085013567ffffffffffffffff811115611989575f80fd5b6119958782880161182f565b95989497509550505050565b5f805f805f85870360e08112156119b6575f80fd5b8635955060208701356119c881611800565b945060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0820112156119f9575f80fd5b5060408601925060c086013567ffffffffffffffff811115611a19575f80fd5b611a258882890161182f565b969995985093965092949392505050565b5f60208284031215611a46575f80fd5b5051919050565b5f8060408385031215611a5e575f80fd5b8235611a6981611800565b946020939093013593505050565b5f60208284031215611a87575f80fd5b81518015158114611663575f80fd5b5f60208284031215611aa6575f80fd5b813560ff81168114611663575f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b6080810181855f5b6002811015611b0a578151835260209283019290910190600101611aeb565b5050508360408301526001600160a01b0383166060830152949350505050565b60a0810181855f5b6003811015611b51578151835260209283019290910190600101611b32565b5050508360608301526001600160a01b0383166080830152949350505050565b5f82515f5b81811015611b905760208186018101518583015201611b76565b505f92019182525091905056fea26469706673582212204ca22089d0ccae63adfa7019c471f5ed822bd269588844b5a65dedfcaa83ddeb64736f6c63430008140033000000000000000000000000926df14a23be491164dcf93f4c468a50ef659d5b000000000000000000000000e3ed95e130ad9e15643f5a5f232a3dae980784cd000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4
Deployed Bytecode
0x608060405234801561000f575f80fd5b5060043610610115575f3560e01c80637062b6cf116100ad578063a04b9ccb1161007d578063db91323611610063578063db913236146102fb578063efdf0bb01461030e578063f0c9e46514610321575f80fd5b8063a04b9ccb146102d5578063a8149fdd146102e8575f80fd5b80637062b6cf146101dd5780638a0dac4a146101f05780638e8f294b1461020357806392c592d0146102ae575f80fd5b80633585e70f116100e85780633585e70f14610191578063452a9320146101a45780634ee54921146101b757806356fc300c146101ca575f80fd5b806301681a621461011957806312d43a511461012e578063252408101461015d5780632cfa26df14610170575b5f80fd5b61012c610127366004611814565b610329565b005b5f54610140906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b600154610140906001600160a01b031681565b61018361017e366004611874565b61040a565b604051908152602001610154565b61012c61019f3660046118e0565b61041f565b600254610140906001600160a01b031681565b6101836101c5366004611874565b61055f565b6101836101d8366004611949565b61056c565b6101836101eb366004611949565b610702565b61012c6101fe366004611814565b6109ba565b610267610211366004611814565b60036020525f90815260409020805460018201546002909201546001600160a01b03918216926fffffffffffffffffffffffffffffffff8082169370010000000000000000000000000000000090920416911684565b60405161015494939291906001600160a01b0394851681526fffffffffffffffffffffffffffffffff93841660208201529190921660408201529116606082015260800190565b6101407f000000000000000000000000865377367054516e17014ccded1e7d814edc9ce481565b6101836102e33660046119a1565b610a6a565b6101836102f6366004611949565b610cfb565b61012c610309366004611814565b610e3b565b61012c61031c366004611814565b610f11565b61012c610fba565b5f546001600160a01b0316331461036c576040517fb577c1f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f546040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152610407916001600160a01b0390811691908416906370a0823190602401602060405180830381865afa1580156103d2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103f69190611a36565b6001600160a01b0384169190611073565b50565b5f6104178433858561056c565b949350505050565b5f546001600160a01b03163314610462576040517fb577c1f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516080810182526001600160a01b038681168083526fffffffffffffffffffffffffffffffff8781166020808601828152898416878901908152898716606089018181528f89165f818152600387528c90209a518b54908b167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216178c5594519351881670010000000000000000000000000000000002939097169290921760018a0155905160029098018054989097169790911696909617909455945194855292939092917f57fbc79da3742e8259f8c34c2ed0447c05a5fe4e7b1657d29b74128d03badbe4910160405180910390a45050505050565b5f61041784338585610cfb565b5f808061057b84860186611a4d565b91509150610588826110ec565b6001600160a01b038083165f908152600360205260409020600201541680156106ea575f6105b8848a853061114c565b6001600160a01b038581165f90815260036020526040908190205490517f095ea7b3000000000000000000000000000000000000000000000000000000008152858316600482015260248101849052929350169063095ea7b3906044016020604051808303815f875af1158015610631573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106559190611a77565b506040517f6e553f65000000000000000000000000000000000000000000000000000000008152600481018290526001600160a01b038981166024830152831690636e553f65906044016020604051808303815f875af11580156106bb573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106df9190611a36565b945050505050610417565b6106f68389848a61114c565b98975050505050505050565b5f8061071083850185611a4d565b50905061071c816110ec565b5f6107298730878761056c565b6001600160a01b038084165f908152600360205260408120600201549293509116908082156107db57506040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015282906001600160a01b038216906370a0823190602401602060405180830381865afa1580156107b0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107d49190611a36565b9150610872565b506001600160a01b038481165f90815260036020526040908190205490517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015291169081906370a0823190602401602060405180830381865afa15801561084b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061086f9190611a36565b91505b818411156108ac576040517f3999656700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b0386811660048301526024820184905282169063095ea7b3906044016020604051808303815f875af1158015610911573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109359190611a77565b506040517f47e7ef240000000000000000000000000000000000000000000000000000000081526001600160a01b038a81166004830152602482018490528616906347e7ef24906044015f604051808303815f87803b158015610996575f80fd5b505af11580156109a8573d5f803e3d5ffd5b50939c9b505050505050505050505050565b5f546001600160a01b031633146109fd576040517fb577c1f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527fb6182387b7ea948602a7e04e662a27ce251dc3dd014eacaed10dce36b41bf1a5906020015b60405180910390a150565b5f8080610a7984860186611a4d565b91509150610a86826110ec565b6001600160a01b038216633525f591338a8935610aa960408c0160208d01611a96565b604080517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b1681526001600160a01b0390951660048601526024850193909352604484019190915260ff1660648301528901356084820152606089013560a482015260c4015f604051808303815f87803b158015610b2a575f80fd5b505af1158015610b3c573d5f803e3d5ffd5b5050506001600160a01b038084165f90815260036020526040902080546002909101549082169250168015610bf8576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018b90526001600160a01b03821690632e1a7d4d906024016020604051808303815f875af1158015610bc8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bec9190611a36565b9950610bf8818a611459565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201528a906001600160a01b038416906370a0823190602401602060405180830381865afa158015610c55573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c799190611a36565b1015610cb1576040517f955a8bf500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0384165f90815260036020526040902060010154610ced9083908c906fffffffffffffffffffffffffffffffff16868d6114f6565b9a9950505050505050505050565b5f8080610d0a84860186611a4d565b91509150610d17826110ec565b6001600160a01b038083165f908152600360205260408120805460028201546001909201549084169391909116916fffffffffffffffffffffffffffffffff909116908215610e0757610d756001600160a01b03841633308e61159d565b6040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018c90526001600160a01b03841690632e1a7d4d906024016020604051808303815f875af1158015610dd2573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610df69190611a36565b9050610e02838b611459565b610e1f565b610e1c6001600160a01b03851633308e61159d565b50895b610e2c848284888e6114f6565b9b9a5050505050505050505050565b6002546001600160a01b031633141580610e5f57505f546001600160a01b03163314155b15610e96576040517fe6000c8500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381165f8181526003602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000908116825560018201849055600290910180549091169055517f59d7b1e52008dc342c9421dadfc773114b914a65682a4e4b53cf60a970df0d779190a250565b5f546001600160a01b03163314610f54576040517fb577c1f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527ff74ae56780e3765c0c0897ef57fb50a10a237584f419631812daf040913e1c9f90602001610a5f565b6001546001600160a01b03163314610ffe576040517f7c04d72b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180545f80546001600160a01b0383167fffffffffffffffffffffffff000000000000000000000000000000000000000091821681179092559091169091556040519081527f639717155292ce2c3e699929a8b65d14a637640f75ab5b6d165a4e735d82a4559060200160405180910390a1565b6040516001600160a01b038381166024830152604482018390526110e791859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506115dc565b505050565b6001600160a01b038181165f9081526003602052604090205416610407576040517f31589d090000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024015b60405180910390fd5b5f6111826001600160a01b037f000000000000000000000000865377367054516e17014ccded1e7d814edc9ce41633308761159d565b6001600160a01b038581165f90815260036020526040908190206001810154905491517f095ea7b300000000000000000000000000000000000000000000000000000000815291831660048301819052602483018890526fffffffffffffffffffffffffffffffff9091169290917f000000000000000000000000865377367054516e17014ccded1e7d814edc9ce49091169063095ea7b3906044016020604051808303815f875af115801561123a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061125e9190611a77565b506001600160a01b0387165f9081526003602052604090206001015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16600203611362576112af6117c4565b8681846fffffffffffffffffffffffffffffffff16600281106112d4576112d4611ab6565b60200201526040517f0c3e4b540000000000000000000000000000000000000000000000000000000081526001600160a01b03831690630c3e4b54906113229084908a908a90600401611ae3565b6020604051808303815f875af115801561133e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106f69190611a36565b6001600160a01b0387165f9081526003602081905260409091206001015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff169003611427576113b46117e2565b8681846fffffffffffffffffffffffffffffffff16600381106113d9576113d9611ab6565b60200201526040517f75b96abc0000000000000000000000000000000000000000000000000000000081526001600160a01b038316906375b96abc906113229084908a908a90600401611b2a565b6040517fd623472500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f906001600160a01b038416906370a0823190602401602060405180830381865afa1580156114b6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114da9190611a36565b905080156110e7576110e76001600160a01b0384168383611073565b6040517f081579a500000000000000000000000000000000000000000000000000000000815260048101859052600f84900b6024820152604481018390526001600160a01b0382811660648301525f919087169063081579a5906084016020604051808303815f875af115801561156f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115939190611a36565b9695505050505050565b6040516001600160a01b0384811660248301528381166044830152606482018390526115d69186918216906323b872dd906084016110a0565b50505050565b5f6115f06001600160a01b03841683611656565b905080515f141580156116145750808060200190518101906116129190611a77565b155b156110e7576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401611143565b606061166383835f61166a565b9392505050565b6060814710156116a8576040517fcd786059000000000000000000000000000000000000000000000000000000008152306004820152602401611143565b5f80856001600160a01b031684866040516116c39190611b71565b5f6040518083038185875af1925050503d805f81146116fd576040519150601f19603f3d011682016040523d82523d5f602084013e611702565b606091505b50915091506115938683836060826117225761171d82611782565b611663565b815115801561173957506001600160a01b0384163b155b1561177b576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401611143565b5080611663565b8051156117925780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052806002906020820280368337509192915050565b60405180606001604052806003906020820280368337509192915050565b6001600160a01b0381168114610407575f80fd5b5f60208284031215611824575f80fd5b813561166381611800565b5f8083601f84011261183f575f80fd5b50813567ffffffffffffffff811115611856575f80fd5b60208301915083602082850101111561186d575f80fd5b9250929050565b5f805f60408486031215611886575f80fd5b83359250602084013567ffffffffffffffff8111156118a3575f80fd5b6118af8682870161182f565b9497909650939450505050565b80356fffffffffffffffffffffffffffffffff811681146118db575f80fd5b919050565b5f805f805f60a086880312156118f4575f80fd5b85356118ff81611800565b9450602086013561190f81611800565b935061191d604087016118bc565b925061192b606087016118bc565b9150608086013561193b81611800565b809150509295509295909350565b5f805f806060858703121561195c575f80fd5b84359350602085013561196e81611800565b9250604085013567ffffffffffffffff811115611989575f80fd5b6119958782880161182f565b95989497509550505050565b5f805f805f85870360e08112156119b6575f80fd5b8635955060208701356119c881611800565b945060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0820112156119f9575f80fd5b5060408601925060c086013567ffffffffffffffff811115611a19575f80fd5b611a258882890161182f565b969995985093965092949392505050565b5f60208284031215611a46575f80fd5b5051919050565b5f8060408385031215611a5e575f80fd5b8235611a6981611800565b946020939093013593505050565b5f60208284031215611a87575f80fd5b81518015158114611663575f80fd5b5f60208284031215611aa6575f80fd5b813560ff81168114611663575f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b6080810181855f5b6002811015611b0a578151835260209283019290910190600101611aeb565b5050508360408301526001600160a01b0383166060830152949350505050565b60a0810181855f5b6003811015611b51578151835260209283019290910190600101611b32565b5050508360608301526001600160a01b0383166080830152949350505050565b5f82515f5b81811015611b905760208186018101518583015201611b76565b505f92019182525091905056fea26469706673582212204ca22089d0ccae63adfa7019c471f5ed822bd269588844b5a65dedfcaa83ddeb64736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000926df14a23be491164dcf93f4c468a50ef659d5b000000000000000000000000e3ed95e130ad9e15643f5a5f232a3dae980784cd000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4
-----Decoded View---------------
Arg [0] : _gov (address): 0x926dF14a23BE491164dCF93f4c468A50ef659D5B
Arg [1] : _guardian (address): 0xE3eD95e130ad9E15643f5A5f232a3daE980784cd
Arg [2] : _dola (address): 0x865377367054516e17014CcdED1e7d814EDC9ce4
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000926df14a23be491164dcf93f4c468a50ef659d5b
Arg [1] : 000000000000000000000000e3ed95e130ad9e15643f5a5f232a3dae980784cd
Arg [2] : 000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
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.