Feature Tip: Add private address tag to any address under My Name Tag !
Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Transfer Governa... | 21084535 | 349 days ago | IN | 0 ETH | 0.00034594 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
Optimizer
Compiler Version
v0.8.19+commit.7dd6d404
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2024-10-31 */ // SPDX-License-Identifier: GPL-3.0 pragma solidity =0.8.19 ^0.8.4; // node_modules/solady/src/tokens/ERC20.sol /// @notice Simple ERC20 + EIP-2612 implementation. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol) /// /// @dev Note: /// - The ERC20 standard allows minting and transferring to and from the zero address, /// minting and transferring zero tokens, as well as self-approvals. /// For performance, this implementation WILL NOT revert for such actions. /// Please add any checks with overrides if desired. /// - The `permit` function uses the ecrecover precompile (0x1). /// /// If you are overriding: /// - NEVER violate the ERC20 invariant: /// the total sum of all balances must be equal to `totalSupply()`. /// - Check that the overridden function is actually used in the function you want to /// change the behavior of. Much of the code has been manually inlined for performance. abstract contract ERC20 { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The total supply has overflowed. error TotalSupplyOverflow(); /// @dev The allowance has overflowed. error AllowanceOverflow(); /// @dev The allowance has underflowed. error AllowanceUnderflow(); /// @dev Insufficient balance. error InsufficientBalance(); /// @dev Insufficient allowance. error InsufficientAllowance(); /// @dev The permit is invalid. error InvalidPermit(); /// @dev The permit has expired. error PermitExpired(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Emitted when `amount` tokens is transferred from `from` to `to`. event Transfer(address indexed from, address indexed to, uint256 amount); /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`. event Approval(address indexed owner, address indexed spender, uint256 amount); /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`. uint256 private constant _TRANSFER_EVENT_SIGNATURE = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`. uint256 private constant _APPROVAL_EVENT_SIGNATURE = 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The storage slot for the total supply. uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c; /// @dev The balance slot of `owner` is given by: /// ``` /// mstore(0x0c, _BALANCE_SLOT_SEED) /// mstore(0x00, owner) /// let balanceSlot := keccak256(0x0c, 0x20) /// ``` uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2; /// @dev The allowance slot of (`owner`, `spender`) is given by: /// ``` /// mstore(0x20, spender) /// mstore(0x0c, _ALLOWANCE_SLOT_SEED) /// mstore(0x00, owner) /// let allowanceSlot := keccak256(0x0c, 0x34) /// ``` uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20; /// @dev The nonce slot of `owner` is given by: /// ``` /// mstore(0x0c, _NONCES_SLOT_SEED) /// mstore(0x00, owner) /// let nonceSlot := keccak256(0x0c, 0x20) /// ``` uint256 private constant _NONCES_SLOT_SEED = 0x38377508; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`. uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901; /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`. bytes32 private constant _DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; /// @dev `keccak256("1")`. bytes32 private constant _VERSION_HASH = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6; /// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`. bytes32 private constant _PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC20 METADATA */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the name of the token. function name() public view virtual returns (string memory); /// @dev Returns the symbol of the token. function symbol() public view virtual returns (string memory); /// @dev Returns the decimals places of the token. function decimals() public view virtual returns (uint8) { return 18; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC20 */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the amount of tokens in existence. function totalSupply() public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { result := sload(_TOTAL_SUPPLY_SLOT) } } /// @dev Returns the amount of tokens owned by `owner`. function balanceOf(address owner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { mstore(0x0c, _BALANCE_SLOT_SEED) mstore(0x00, owner) result := sload(keccak256(0x0c, 0x20)) } } /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`. function allowance(address owner, address spender) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { mstore(0x20, spender) mstore(0x0c, _ALLOWANCE_SLOT_SEED) mstore(0x00, owner) result := sload(keccak256(0x0c, 0x34)) } } /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens. /// /// Emits a {Approval} event. function approve(address spender, uint256 amount) public virtual returns (bool) { /// @solidity memory-safe-assembly assembly { // Compute the allowance slot and store the amount. mstore(0x20, spender) mstore(0x0c, _ALLOWANCE_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x34), amount) // Emit the {Approval} event. mstore(0x00, amount) log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c))) } return true; } /// @dev Transfer `amount` tokens from the caller to `to`. /// /// Requirements: /// - `from` must at least have `amount`. /// /// Emits a {Transfer} event. function transfer(address to, uint256 amount) public virtual returns (bool) { _beforeTokenTransfer(msg.sender, to, amount); /// @solidity memory-safe-assembly assembly { // Compute the balance slot and load its value. mstore(0x0c, _BALANCE_SLOT_SEED) mstore(0x00, caller()) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Compute the balance slot of `to`. mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance of `to`. // Will not overflow because the sum of all user balances // cannot exceed the maximum uint256 value. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c))) } _afterTokenTransfer(msg.sender, to, amount); return true; } /// @dev Transfers `amount` tokens from `from` to `to`. /// /// Note: Does not update the allowance if it is the maximum uint256 value. /// /// Requirements: /// - `from` must at least have `amount`. /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`. /// /// Emits a {Transfer} event. function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) { _beforeTokenTransfer(from, to, amount); /// @solidity memory-safe-assembly assembly { let from_ := shl(96, from) // Compute the allowance slot and load its value. mstore(0x20, caller()) mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED)) let allowanceSlot := keccak256(0x0c, 0x34) let allowance_ := sload(allowanceSlot) // If the allowance is not the maximum uint256 value. if add(allowance_, 1) { // Revert if the amount to be transferred exceeds the allowance. if gt(amount, allowance_) { mstore(0x00, 0x13be252b) // `InsufficientAllowance()`. revert(0x1c, 0x04) } // Subtract and store the updated allowance. sstore(allowanceSlot, sub(allowance_, amount)) } // Compute the balance slot and load its value. mstore(0x0c, or(from_, _BALANCE_SLOT_SEED)) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Compute the balance slot of `to`. mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance of `to`. // Will not overflow because the sum of all user balances // cannot exceed the maximum uint256 value. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c))) } _afterTokenTransfer(from, to, amount); return true; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EIP-2612 */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev For more performance, override to return the constant value /// of `keccak256(bytes(name()))` if `name()` will never change. function _constantNameHash() internal view virtual returns (bytes32 result) {} /// @dev Returns the current nonce for `owner`. /// This value is used to compute the signature for EIP-2612 permit. function nonces(address owner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // Compute the nonce slot and load its value. mstore(0x0c, _NONCES_SLOT_SEED) mstore(0x00, owner) result := sload(keccak256(0x0c, 0x20)) } } /// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`, /// authorized by a signed approval by `owner`. /// /// Emits a {Approval} event. function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { bytes32 nameHash = _constantNameHash(); // We simply calculate it on-the-fly to allow for cases where the `name` may change. if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name())); /// @solidity memory-safe-assembly assembly { // Revert if the block timestamp is greater than `deadline`. if gt(timestamp(), deadline) { mstore(0x00, 0x1a15a3cc) // `PermitExpired()`. revert(0x1c, 0x04) } let m := mload(0x40) // Grab the free memory pointer. // Clean the upper 96 bits. owner := shr(96, shl(96, owner)) spender := shr(96, shl(96, spender)) // Compute the nonce slot and load its value. mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX) mstore(0x00, owner) let nonceSlot := keccak256(0x0c, 0x20) let nonceValue := sload(nonceSlot) // Prepare the domain separator. mstore(m, _DOMAIN_TYPEHASH) mstore(add(m, 0x20), nameHash) mstore(add(m, 0x40), _VERSION_HASH) mstore(add(m, 0x60), chainid()) mstore(add(m, 0x80), address()) mstore(0x2e, keccak256(m, 0xa0)) // Prepare the struct hash. mstore(m, _PERMIT_TYPEHASH) mstore(add(m, 0x20), owner) mstore(add(m, 0x40), spender) mstore(add(m, 0x60), value) mstore(add(m, 0x80), nonceValue) mstore(add(m, 0xa0), deadline) mstore(0x4e, keccak256(m, 0xc0)) // Prepare the ecrecover calldata. mstore(0x00, keccak256(0x2c, 0x42)) mstore(0x20, and(0xff, v)) mstore(0x40, r) mstore(0x60, s) let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20) // If the ecrecover fails, the returndatasize will be 0x00, // `owner` will be checked if it equals the hash at 0x00, // which evaluates to false (i.e. 0), and we will revert. // If the ecrecover succeeds, the returndatasize will be 0x20, // `owner` will be compared against the returned address at 0x20. if iszero(eq(mload(returndatasize()), owner)) { mstore(0x00, 0xddafbaef) // `InvalidPermit()`. revert(0x1c, 0x04) } // Increment and store the updated nonce. sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds. // Compute the allowance slot and store the value. // The `owner` is already at slot 0x20. mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender)) sstore(keccak256(0x2c, 0x34), value) // Emit the {Approval} event. log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender) mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero pointer. } } /// @dev Returns the EIP-712 domain separator for the EIP-2612 permit. function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) { bytes32 nameHash = _constantNameHash(); // We simply calculate it on-the-fly to allow for cases where the `name` may change. if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name())); /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Grab the free memory pointer. mstore(m, _DOMAIN_TYPEHASH) mstore(add(m, 0x20), nameHash) mstore(add(m, 0x40), _VERSION_HASH) mstore(add(m, 0x60), chainid()) mstore(add(m, 0x80), address()) result := keccak256(m, 0xa0) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL MINT FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Mints `amount` tokens to `to`, increasing the total supply. /// /// Emits a {Transfer} event. function _mint(address to, uint256 amount) internal virtual { _beforeTokenTransfer(address(0), to, amount); /// @solidity memory-safe-assembly assembly { let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT) let totalSupplyAfter := add(totalSupplyBefore, amount) // Revert if the total supply overflows. if lt(totalSupplyAfter, totalSupplyBefore) { mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`. revert(0x1c, 0x04) } // Store the updated total supply. sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter) // Compute the balance slot and load its value. mstore(0x0c, _BALANCE_SLOT_SEED) mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c))) } _afterTokenTransfer(address(0), to, amount); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL BURN FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Burns `amount` tokens from `from`, reducing the total supply. /// /// Emits a {Transfer} event. function _burn(address from, uint256 amount) internal virtual { _beforeTokenTransfer(from, address(0), amount); /// @solidity memory-safe-assembly assembly { // Compute the balance slot and load its value. mstore(0x0c, _BALANCE_SLOT_SEED) mstore(0x00, from) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Subtract and store the updated total supply. sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount)) // Emit the {Transfer} event. mstore(0x00, amount) log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0) } _afterTokenTransfer(from, address(0), amount); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL TRANSFER FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Moves `amount` of tokens from `from` to `to`. function _transfer(address from, address to, uint256 amount) internal virtual { _beforeTokenTransfer(from, to, amount); /// @solidity memory-safe-assembly assembly { let from_ := shl(96, from) // Compute the balance slot and load its value. mstore(0x0c, or(from_, _BALANCE_SLOT_SEED)) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Compute the balance slot of `to`. mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance of `to`. // Will not overflow because the sum of all user balances // cannot exceed the maximum uint256 value. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c))) } _afterTokenTransfer(from, to, amount); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL ALLOWANCE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Updates the allowance of `owner` for `spender` based on spent `amount`. function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { /// @solidity memory-safe-assembly assembly { // Compute the allowance slot and load its value. mstore(0x20, spender) mstore(0x0c, _ALLOWANCE_SLOT_SEED) mstore(0x00, owner) let allowanceSlot := keccak256(0x0c, 0x34) let allowance_ := sload(allowanceSlot) // If the allowance is not the maximum uint256 value. if add(allowance_, 1) { // Revert if the amount to be transferred exceeds the allowance. if gt(amount, allowance_) { mstore(0x00, 0x13be252b) // `InsufficientAllowance()`. revert(0x1c, 0x04) } // Subtract and store the updated allowance. sstore(allowanceSlot, sub(allowance_, amount)) } } } /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`. /// /// Emits a {Approval} event. function _approve(address owner, address spender, uint256 amount) internal virtual { /// @solidity memory-safe-assembly assembly { let owner_ := shl(96, owner) // Compute the allowance slot and store the amount. mstore(0x20, spender) mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED)) sstore(keccak256(0x0c, 0x34), amount) // Emit the {Approval} event. mstore(0x00, amount) log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c))) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HOOKS TO OVERRIDE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Hook that is called before any transfer of tokens. /// This includes minting and burning. function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} /// @dev Hook that is called after any transfer of tokens. /// This includes minting and burning. function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} } // node_modules/solady/src/utils/FixedPointMathLib.sol /// @notice Arithmetic library with operations for fixed-point numbers. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol) library FixedPointMathLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The operation failed, as the output exceeds the maximum value of uint256. error ExpOverflow(); /// @dev The operation failed, as the output exceeds the maximum value of uint256. error FactorialOverflow(); /// @dev The operation failed, due to an overflow. error RPowOverflow(); /// @dev The mantissa is too big to fit. error MantissaOverflow(); /// @dev The operation failed, due to an multiplication overflow. error MulWadFailed(); /// @dev The operation failed, due to an multiplication overflow. error SMulWadFailed(); /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero. error DivWadFailed(); /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero. error SDivWadFailed(); /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero. error MulDivFailed(); /// @dev The division failed, as the denominator is zero. error DivFailed(); /// @dev The full precision multiply-divide operation failed, either due /// to the result being larger than 256 bits, or a division by a zero. error FullMulDivFailed(); /// @dev The output is undefined, as the input is less-than-or-equal to zero. error LnWadUndefined(); /// @dev The input outside the acceptable domain. error OutOfDomain(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The scalar of ETH and most ERC20s. uint256 internal constant WAD = 1e18; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* SIMPLIFIED FIXED POINT OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Equivalent to `(x * y) / WAD` rounded down. function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`. if mul(y, gt(x, div(not(0), y))) { mstore(0x00, 0xbac65e5b) // `MulWadFailed()`. revert(0x1c, 0x04) } z := div(mul(x, y), WAD) } } /// @dev Equivalent to `(x * y) / WAD` rounded down. function sMulWad(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := mul(x, y) // Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`. if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) { mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`. revert(0x1c, 0x04) } z := sdiv(z, WAD) } } /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks. function rawMulWad(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := div(mul(x, y), WAD) } } /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks. function rawSMulWad(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := sdiv(mul(x, y), WAD) } } /// @dev Equivalent to `(x * y) / WAD` rounded up. function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`. if mul(y, gt(x, div(not(0), y))) { mstore(0x00, 0xbac65e5b) // `MulWadFailed()`. revert(0x1c, 0x04) } z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD)) } } /// @dev Equivalent to `(x * y) / WAD` rounded up, but without overflow checks. function rawMulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD)) } } /// @dev Equivalent to `(x * WAD) / y` rounded down. function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`. if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) { mstore(0x00, 0x7c5f487d) // `DivWadFailed()`. revert(0x1c, 0x04) } z := div(mul(x, WAD), y) } } /// @dev Equivalent to `(x * WAD) / y` rounded down. function sDivWad(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := mul(x, WAD) // Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`. if iszero(and(iszero(iszero(y)), eq(sdiv(z, WAD), x))) { mstore(0x00, 0x5c43740d) // `SDivWadFailed()`. revert(0x1c, 0x04) } z := sdiv(mul(x, WAD), y) } } /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks. function rawDivWad(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := div(mul(x, WAD), y) } } /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks. function rawSDivWad(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := sdiv(mul(x, WAD), y) } } /// @dev Equivalent to `(x * WAD) / y` rounded up. function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`. if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) { mstore(0x00, 0x7c5f487d) // `DivWadFailed()`. revert(0x1c, 0x04) } z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y)) } } /// @dev Equivalent to `(x * WAD) / y` rounded up, but without overflow and divide by zero checks. function rawDivWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y)) } } /// @dev Equivalent to `x` to the power of `y`. /// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`. function powWad(int256 x, int256 y) internal pure returns (int256) { // Using `ln(x)` means `x` must be greater than 0. return expWad((lnWad(x) * y) / int256(WAD)); } /// @dev Returns `exp(x)`, denominated in `WAD`. /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln function expWad(int256 x) internal pure returns (int256 r) { unchecked { // When the result is less than 0.5 we return zero. // This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`. if (x <= -41446531673892822313) return r; /// @solidity memory-safe-assembly assembly { // When the result is greater than `(2**255 - 1) / 1e18` we can not represent it as // an int. This happens when `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`. if iszero(slt(x, 135305999368893231589)) { mstore(0x00, 0xa37bfec9) // `ExpOverflow()`. revert(0x1c, 0x04) } } // `x` is now in the range `(-42, 136) * 1e18`. Convert to `(-42, 136) * 2**96` // for more intermediate precision and a binary basis. This base conversion // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78. x = (x << 78) / 5 ** 18; // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers // of two such that exp(x) = exp(x') * 2**k, where k is an integer. // Solving this gives k = round(x / log(2)) and x' = x - k * log(2). int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96; x = x - k * 54916777467707473351141471128; // `k` is in the range `[-61, 195]`. // Evaluate using a (6, 7)-term rational approximation. // `p` is made monic, we'll multiply by a scale factor later. int256 y = x + 1346386616545796478920950773328; y = ((y * x) >> 96) + 57155421227552351082224309758442; int256 p = y + x - 94201549194550492254356042504812; p = ((p * y) >> 96) + 28719021644029726153956944680412240; p = p * x + (4385272521454847904659076985693276 << 96); // We leave `p` in `2**192` basis so we don't need to scale it back up for the division. int256 q = x - 2855989394907223263936484059900; q = ((q * x) >> 96) + 50020603652535783019961831881945; q = ((q * x) >> 96) - 533845033583426703283633433725380; q = ((q * x) >> 96) + 3604857256930695427073651918091429; q = ((q * x) >> 96) - 14423608567350463180887372962807573; q = ((q * x) >> 96) + 26449188498355588339934803723976023; /// @solidity memory-safe-assembly assembly { // Div in assembly because solidity adds a zero check despite the unchecked. // The q polynomial won't have zeros in the domain as all its roots are complex. // No scaling is necessary because p is already `2**96` too large. r := sdiv(p, q) } // r should be in the range `(0.09, 0.25) * 2**96`. // We now need to multiply r by: // - The scale factor `s ≈ 6.031367120`. // - The `2**k` factor from the range reduction. // - The `1e18 / 2**96` factor for base conversion. // We do this all at once, with an intermediate result in `2**213` // basis, so the final right shift is always by a positive amount. r = int256( (uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k) ); } } /// @dev Returns `ln(x)`, denominated in `WAD`. /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln function lnWad(int256 x) internal pure returns (int256 r) { /// @solidity memory-safe-assembly assembly { // We want to convert `x` from `10**18` fixed point to `2**96` fixed point. // We do this by multiplying by `2**96 / 10**18`. But since // `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here // and add `ln(2**96 / 10**18)` at the end. // Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 ^ log2(x)`. r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffff, shr(r, x)))) r := or(r, shl(3, lt(0xff, shr(r, x)))) // We place the check here for more optimal stack operations. if iszero(sgt(x, 0)) { mstore(0x00, 0x1615e638) // `LnWadUndefined()`. revert(0x1c, 0x04) } // forgefmt: disable-next-item r := xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)), 0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff)) // Reduce range of x to (1, 2) * 2**96 // ln(2^k * x) = k * ln(2) + ln(x) x := shr(159, shl(r, x)) // Evaluate using a (8, 8)-term rational approximation. // `p` is made monic, we will multiply by a scale factor later. // forgefmt: disable-next-item let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir. sar(96, mul(add(43456485725739037958740375743393, sar(96, mul(add(24828157081833163892658089445524, sar(96, mul(add(3273285459638523848632254066296, x), x))), x))), x)), 11111509109440967052023855526967) p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857) p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526) p := sub(mul(p, x), shl(96, 795164235651350426258249787498)) // We leave `p` in `2**192` basis so we don't need to scale it back up for the division. // `q` is monic by convention. let q := add(5573035233440673466300451813936, x) q := add(71694874799317883764090561454958, sar(96, mul(x, q))) q := add(283447036172924575727196451306956, sar(96, mul(x, q))) q := add(401686690394027663651624208769553, sar(96, mul(x, q))) q := add(204048457590392012362485061816622, sar(96, mul(x, q))) q := add(31853899698501571402653359427138, sar(96, mul(x, q))) q := add(909429971244387300277376558375, sar(96, mul(x, q))) // `p / q` is in the range `(0, 0.125) * 2**96`. // Finalization, we need to: // - Multiply by the scale factor `s = 5.549…`. // - Add `ln(2**96 / 10**18)`. // - Add `k * ln(2)`. // - Multiply by `10**18 / 2**96 = 5**18 >> 78`. // The q polynomial is known not to have zeros in the domain. // No scaling required because p is already `2**96` too large. p := sdiv(p, q) // Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`. p := mul(1677202110996718588342820967067443963516166, p) // Add `ln(2) * k * 5**18 * 2**192`. // forgefmt: disable-next-item p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p) // Add `ln(2**96 / 10**18) * 5**18 * 2**192`. p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p) // Base conversion: mul `2**18 / 2**192`. r := sar(174, p) } } /// @dev Returns `W_0(x)`, denominated in `WAD`. /// See: https://en.wikipedia.org/wiki/Lambert_W_function /// a.k.a. Product log function. This is an approximation of the principal branch. function lambertW0Wad(int256 x) internal pure returns (int256 w) { // forgefmt: disable-next-item unchecked { if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`. int256 wad = int256(WAD); int256 p = x; uint256 c; // Whether we need to avoid catastrophic cancellation. uint256 i = 4; // Number of iterations. if (w <= 0x1ffffffffffff) { if (-0x4000000000000 <= w) { i = 1; // Inputs near zero only take one step to converge. } else if (w <= -0x3ffffffffffffff) { i = 32; // Inputs near `-1/e` take very long to converge. } } else if (w >> 63 == 0) { /// @solidity memory-safe-assembly assembly { // Inline log2 for more performance, since the range is small. let v := shr(49, w) let l := shl(3, lt(0xff, v)) l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)), 0x0706060506020504060203020504030106050205030304010505030400000000)), 49) w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13)) c := gt(l, 60) i := add(2, add(gt(l, 53), c)) } } else { int256 ll = lnWad(w = lnWad(w)); /// @solidity memory-safe-assembly assembly { // `w = ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`. w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll)) i := add(3, iszero(shr(68, x))) c := iszero(shr(143, x)) } if (c == 0) { do { // If `x` is big, use Newton's so that intermediate values won't overflow. int256 e = expWad(w); /// @solidity memory-safe-assembly assembly { let t := mul(w, div(e, wad)) w := sub(w, sdiv(sub(t, x), div(add(e, t), wad))) } if (p <= w) break; p = w; } while (--i != 0); /// @solidity memory-safe-assembly assembly { w := sub(w, sgt(w, 2)) } return w; } } do { // Otherwise, use Halley's for faster convergence. int256 e = expWad(w); /// @solidity memory-safe-assembly assembly { let t := add(w, wad) let s := sub(mul(w, e), mul(x, wad)) w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t))))) } if (p <= w) break; p = w; } while (--i != c); /// @solidity memory-safe-assembly assembly { w := sub(w, sgt(w, 2)) } // For certain ranges of `x`, we'll use the quadratic-rate recursive formula of // R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation. if (c != 0) { int256 t = w | 1; /// @solidity memory-safe-assembly assembly { x := sdiv(mul(x, wad), t) } x = (t * (wad + lnWad(x))); /// @solidity memory-safe-assembly assembly { w := sdiv(x, add(wad, t)) } } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* GENERAL NUMBER UTILITIES */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Calculates `floor(x * y / d)` with full precision. /// Throws if result overflows a uint256 or when `d` is zero. /// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for {} 1 {} { // 512-bit multiply `[p1 p0] = x * y`. // Compute the product mod `2**256` and mod `2**256 - 1` // then use the Chinese Remainder Theorem to reconstruct // the 512 bit result. The result is stored in two 256 // variables such that `product = p1 * 2**256 + p0`. // Least significant 256 bits of the product. result := mul(x, y) // Temporarily use `result` as `p0` to save gas. let mm := mulmod(x, y, not(0)) // Most significant 256 bits of the product. let p1 := sub(mm, add(result, lt(mm, result))) // Handle non-overflow cases, 256 by 256 division. if iszero(p1) { if iszero(d) { mstore(0x00, 0xae47f702) // `FullMulDivFailed()`. revert(0x1c, 0x04) } result := div(result, d) break } // Make sure the result is less than `2**256`. Also prevents `d == 0`. if iszero(gt(d, p1)) { mstore(0x00, 0xae47f702) // `FullMulDivFailed()`. revert(0x1c, 0x04) } /*------------------- 512 by 256 division --------------------*/ // Make division exact by subtracting the remainder from `[p1 p0]`. // Compute remainder using mulmod. let r := mulmod(x, y, d) // `t` is the least significant bit of `d`. // Always greater or equal to 1. let t := and(d, sub(0, d)) // Divide `d` by `t`, which is a power of two. d := div(d, t) // Invert `d mod 2**256` // Now that `d` is an odd number, it has an inverse // modulo `2**256` such that `d * inv = 1 mod 2**256`. // Compute the inverse by starting with a seed that is correct // correct for four bits. That is, `d * inv = 1 mod 2**4`. let inv := xor(2, mul(3, d)) // Now use Newton-Raphson iteration to improve the precision. // Thanks to Hensel's lifting lemma, this also works in modular // arithmetic, doubling the correct bits in each step. inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8 inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16 inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32 inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64 inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128 result := mul( // Divide [p1 p0] by the factors of two. // Shift in bits from `p1` into `p0`. For this we need // to flip `t` such that it is `2**256 / t`. or( mul(sub(p1, gt(r, result)), add(div(sub(0, t), t), 1)), div(sub(result, r), t) ), // inverse mod 2**256 mul(inv, sub(2, mul(d, inv))) ) break } } } /// @dev Calculates `floor(x * y / d)` with full precision, rounded up. /// Throws if result overflows a uint256 or when `d` is zero. /// Credit to Uniswap-v3-core under MIT license: /// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) { result = fullMulDiv(x, y, d); /// @solidity memory-safe-assembly assembly { if mulmod(x, y, d) { result := add(result, 1) if iszero(result) { mstore(0x00, 0xae47f702) // `FullMulDivFailed()`. revert(0x1c, 0x04) } } } } /// @dev Returns `floor(x * y / d)`. /// Reverts if `x * y` overflows, or `d` is zero. function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) { mstore(0x00, 0xad251c27) // `MulDivFailed()`. revert(0x1c, 0x04) } z := div(mul(x, y), d) } } /// @dev Returns `ceil(x * y / d)`. /// Reverts if `x * y` overflows, or `d` is zero. function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) { mstore(0x00, 0xad251c27) // `MulDivFailed()`. revert(0x1c, 0x04) } z := add(iszero(iszero(mod(mul(x, y), d))), div(mul(x, y), d)) } } /// @dev Returns `ceil(x / d)`. /// Reverts if `d` is zero. function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { if iszero(d) { mstore(0x00, 0x65244e4e) // `DivFailed()`. revert(0x1c, 0x04) } z := add(iszero(iszero(mod(x, d))), div(x, d)) } } /// @dev Returns `max(0, x - y)`. function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := mul(gt(x, y), sub(x, y)) } } /// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`. /// Reverts if the computation overflows. function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`. if x { z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x` let half := shr(1, b) // Divide `b` by 2. // Divide `y` by 2 every iteration. for { y := shr(1, y) } y { y := shr(1, y) } { let xx := mul(x, x) // Store x squared. let xxRound := add(xx, half) // Round to the nearest number. // Revert if `xx + half` overflowed, or if `x ** 2` overflows. if or(lt(xxRound, xx), shr(128, x)) { mstore(0x00, 0x49f7642b) // `RPowOverflow()`. revert(0x1c, 0x04) } x := div(xxRound, b) // Set `x` to scaled `xxRound`. // If `y` is odd: if and(y, 1) { let zx := mul(z, x) // Compute `z * x`. let zxRound := add(zx, half) // Round to the nearest number. // If `z * x` overflowed or `zx + half` overflowed: if or(xor(div(zx, x), z), lt(zxRound, zx)) { // Revert if `x` is non-zero. if iszero(iszero(x)) { mstore(0x00, 0x49f7642b) // `RPowOverflow()`. revert(0x1c, 0x04) } } z := div(zxRound, b) // Return properly scaled `zxRound`. } } } } } /// @dev Returns the square root of `x`. function sqrt(uint256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`. z := 181 // The "correct" value is 1, but this saves a multiplication later. // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. // Let `y = x / 2**r`. We check `y >= 2**(k + 8)` // but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`. let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x)) r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x)))) r := or(r, shl(5, lt(0xffffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffffff, shr(r, x)))) z := shl(shr(1, r), z) // Goal was to get `z*z*y` within a small factor of `x`. More iterations could // get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`. // We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small. // That's not possible if `x < 256` but we can just verify those cases exhaustively. // Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`. // Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`. // Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps. // For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)` // is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`, // with largest error when `s = 1` and when `s = 256` or `1/256`. // Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`. // Then we can estimate `sqrt(y)` using // `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`. // There is no overflow risk here since `y < 2**136` after the first branch above. z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181. // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) // If `x+1` is a perfect square, the Babylonian method cycles between // `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor. // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division z := sub(z, lt(div(x, z), z)) } } /// @dev Returns the cube root of `x`. /// Credit to bout3fiddy and pcaversaccio under AGPLv3 license: /// https://github.com/pcaversaccio/snekmate/blob/main/src/utils/Math.vy function cbrt(uint256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffff, shr(r, x)))) r := or(r, shl(3, lt(0xff, shr(r, x)))) z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 3))) z := div(add(add(div(x, mul(z, z)), z), z), 3) z := div(add(add(div(x, mul(z, z)), z), z), 3) z := div(add(add(div(x, mul(z, z)), z), z), 3) z := div(add(add(div(x, mul(z, z)), z), z), 3) z := div(add(add(div(x, mul(z, z)), z), z), 3) z := div(add(add(div(x, mul(z, z)), z), z), 3) z := div(add(add(div(x, mul(z, z)), z), z), 3) z := sub(z, lt(div(x, mul(z, z)), z)) } } /// @dev Returns the square root of `x`, denominated in `WAD`. function sqrtWad(uint256 x) internal pure returns (uint256 z) { unchecked { z = 10 ** 9; if (x <= type(uint256).max / 10 ** 36 - 1) { x *= 10 ** 18; z = 1; } z *= sqrt(x); } } /// @dev Returns the cube root of `x`, denominated in `WAD`. function cbrtWad(uint256 x) internal pure returns (uint256 z) { unchecked { z = 10 ** 12; if (x <= (type(uint256).max / 10 ** 36) * 10 ** 18 - 1) { if (x >= type(uint256).max / 10 ** 36) { x *= 10 ** 18; z = 10 ** 6; } else { x *= 10 ** 36; z = 1; } } z *= cbrt(x); } } /// @dev Returns the factorial of `x`. function factorial(uint256 x) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { if iszero(lt(x, 58)) { mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`. revert(0x1c, 0x04) } for { result := 1 } x { x := sub(x, 1) } { result := mul(result, x) } } } /// @dev Returns the log2 of `x`. /// Equivalent to computing the index of the most significant bit (MSB) of `x`. /// Returns 0 if `x` is zero. function log2(uint256 x) internal pure returns (uint256 r) { /// @solidity memory-safe-assembly assembly { r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffff, shr(r, x)))) r := or(r, shl(3, lt(0xff, shr(r, x)))) // forgefmt: disable-next-item r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)), 0x0706060506020504060203020504030106050205030304010505030400000000)) } } /// @dev Returns the log2 of `x`, rounded up. /// Returns 0 if `x` is zero. function log2Up(uint256 x) internal pure returns (uint256 r) { r = log2(x); /// @solidity memory-safe-assembly assembly { r := add(r, lt(shl(r, 1), x)) } } /// @dev Returns the log10 of `x`. /// Returns 0 if `x` is zero. function log10(uint256 x) internal pure returns (uint256 r) { /// @solidity memory-safe-assembly assembly { if iszero(lt(x, 100000000000000000000000000000000000000)) { x := div(x, 100000000000000000000000000000000000000) r := 38 } if iszero(lt(x, 100000000000000000000)) { x := div(x, 100000000000000000000) r := add(r, 20) } if iszero(lt(x, 10000000000)) { x := div(x, 10000000000) r := add(r, 10) } if iszero(lt(x, 100000)) { x := div(x, 100000) r := add(r, 5) } r := add(r, add(gt(x, 9), add(gt(x, 99), add(gt(x, 999), gt(x, 9999))))) } } /// @dev Returns the log10 of `x`, rounded up. /// Returns 0 if `x` is zero. function log10Up(uint256 x) internal pure returns (uint256 r) { r = log10(x); /// @solidity memory-safe-assembly assembly { r := add(r, lt(exp(10, r), x)) } } /// @dev Returns the log256 of `x`. /// Returns 0 if `x` is zero. function log256(uint256 x) internal pure returns (uint256 r) { /// @solidity memory-safe-assembly assembly { r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffff, shr(r, x)))) r := or(shr(3, r), lt(0xff, shr(r, x))) } } /// @dev Returns the log256 of `x`, rounded up. /// Returns 0 if `x` is zero. function log256Up(uint256 x) internal pure returns (uint256 r) { r = log256(x); /// @solidity memory-safe-assembly assembly { r := add(r, lt(shl(shl(3, r), 1), x)) } } /// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`. /// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent). function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) { /// @solidity memory-safe-assembly assembly { mantissa := x if mantissa { if iszero(mod(mantissa, 1000000000000000000000000000000000)) { mantissa := div(mantissa, 1000000000000000000000000000000000) exponent := 33 } if iszero(mod(mantissa, 10000000000000000000)) { mantissa := div(mantissa, 10000000000000000000) exponent := add(exponent, 19) } if iszero(mod(mantissa, 1000000000000)) { mantissa := div(mantissa, 1000000000000) exponent := add(exponent, 12) } if iszero(mod(mantissa, 1000000)) { mantissa := div(mantissa, 1000000) exponent := add(exponent, 6) } if iszero(mod(mantissa, 10000)) { mantissa := div(mantissa, 10000) exponent := add(exponent, 4) } if iszero(mod(mantissa, 100)) { mantissa := div(mantissa, 100) exponent := add(exponent, 2) } if iszero(mod(mantissa, 10)) { mantissa := div(mantissa, 10) exponent := add(exponent, 1) } } } } /// @dev Convenience function for packing `x` into a smaller number using `sci`. /// The `mantissa` will be in bits [7..255] (the upper 249 bits). /// The `exponent` will be in bits [0..6] (the lower 7 bits). /// Use `SafeCastLib` to safely ensure that the `packed` number is small /// enough to fit in the desired unsigned integer type: /// ``` /// uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether)); /// ``` function packSci(uint256 x) internal pure returns (uint256 packed) { (x, packed) = sci(x); // Reuse for `mantissa` and `exponent`. /// @solidity memory-safe-assembly assembly { if shr(249, x) { mstore(0x00, 0xce30380c) // `MantissaOverflow()`. revert(0x1c, 0x04) } packed := or(shl(7, x), packed) } } /// @dev Convenience function for unpacking a packed number from `packSci`. function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) { unchecked { unpacked = (packed >> 7) * 10 ** (packed & 0x7f); } } /// @dev Returns the average of `x` and `y`. function avg(uint256 x, uint256 y) internal pure returns (uint256 z) { unchecked { z = (x & y) + ((x ^ y) >> 1); } } /// @dev Returns the average of `x` and `y`. function avg(int256 x, int256 y) internal pure returns (int256 z) { unchecked { z = (x >> 1) + (y >> 1) + (x & y & 1); } } /// @dev Returns the absolute value of `x`. function abs(int256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := xor(sub(0, shr(255, x)), add(sub(0, shr(255, x)), x)) } } /// @dev Returns the absolute distance between `x` and `y`. function dist(int256 x, int256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := xor(mul(xor(sub(y, x), sub(x, y)), sgt(x, y)), sub(y, x)) } } /// @dev Returns the minimum of `x` and `y`. function min(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), lt(y, x))) } } /// @dev Returns the minimum of `x` and `y`. function min(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), slt(y, x))) } } /// @dev Returns the maximum of `x` and `y`. function max(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), gt(y, x))) } } /// @dev Returns the maximum of `x` and `y`. function max(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), sgt(y, x))) } } /// @dev Returns `x`, bounded to `minValue` and `maxValue`. function clamp(uint256 x, uint256 minValue, uint256 maxValue) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, minValue), gt(minValue, x))) z := xor(z, mul(xor(z, maxValue), lt(maxValue, z))) } } /// @dev Returns `x`, bounded to `minValue` and `maxValue`. function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, minValue), sgt(minValue, x))) z := xor(z, mul(xor(z, maxValue), slt(maxValue, z))) } } /// @dev Returns greatest common divisor of `x` and `y`. function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { for { z := x } y {} { let t := y y := mod(z, y) z := t } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* RAW NUMBER OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns `x + y`, without checking for overflow. function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) { unchecked { z = x + y; } } /// @dev Returns `x + y`, without checking for overflow. function rawAdd(int256 x, int256 y) internal pure returns (int256 z) { unchecked { z = x + y; } } /// @dev Returns `x - y`, without checking for underflow. function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) { unchecked { z = x - y; } } /// @dev Returns `x - y`, without checking for underflow. function rawSub(int256 x, int256 y) internal pure returns (int256 z) { unchecked { z = x - y; } } /// @dev Returns `x * y`, without checking for overflow. function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) { unchecked { z = x * y; } } /// @dev Returns `x * y`, without checking for overflow. function rawMul(int256 x, int256 y) internal pure returns (int256 z) { unchecked { z = x * y; } } /// @dev Returns `x / y`, returning 0 if `y` is zero. function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := div(x, y) } } /// @dev Returns `x / y`, returning 0 if `y` is zero. function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := sdiv(x, y) } } /// @dev Returns `x % y`, returning 0 if `y` is zero. function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := mod(x, y) } } /// @dev Returns `x % y`, returning 0 if `y` is zero. function rawSMod(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := smod(x, y) } } /// @dev Returns `(x + y) % d`, return 0 if `d` if zero. function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := addmod(x, y, d) } } /// @dev Returns `(x * y) % d`, return 0 if `d` if zero. function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := mulmod(x, y, d) } } } // src/interfaces/IConvexFactory.sol interface IConvexFactory { function protocolFeesPercent() external view returns (uint256); function fallbacks(address gauge) external view returns (address); function create(address token, uint256 pid) external returns (address); } // src/interfaces/IFallback.sol interface IFallback { function initialize() external; function claim(bool _claimExtraRewards) external returns (uint256 rewardTokenAmount, uint256 fallbackRewardTokenAmount, uint256 protocolFees); function balanceOf(address _asset) external view returns (uint256); function deposit(address _asset, uint256 _amount) external; function withdraw(address _asset, uint256 _amount) external; function fallbackRewardToken() external view returns (address); } // src/interfaces/ILiquidityGauge.sol interface ILiquidityGauge { struct Reward { address token; address distributor; uint256 period_finish; uint256 rate; uint256 last_update; uint256 integral; } function deposit_reward_token(address _rewardToken, uint256 _amount) external; function claim_rewards_for(address _user, address _recipient) external; function working_balances(address _address) external view returns (uint256); function deposit(uint256 _value, address _addr) external; function reward_tokens(uint256 _i) external view returns (address); function reward_data(address _tokenReward) external view returns (Reward memory); function balanceOf(address) external view returns (uint256); function totalSupply() external view returns (uint256); function claimable_reward(address _user, address _reward_token) external view returns (uint256); function claimable_tokens(address _user) external returns (uint256); function user_checkpoint(address _user) external returns (bool); function commit_transfer_ownership(address) external; function apply_transfer_ownership() external; function claim_rewards(address) external; function add_reward(address, address) external; function set_claimer(address) external; function admin() external view returns (address); function set_reward_distributor(address _rewardToken, address _newDistrib) external; function lp_token() external view returns (address); function is_killed() external view returns (bool); function staking_token() external view returns (address); } // src/interfaces/IOnlyBoost.sol interface IOnlyBoost { function getOptimalDepositAllocation(address gauge, uint256 amount) external returns (address[] memory, uint256[] memory); function getRebalancedAllocation(address gauge, uint256 amount) external returns (address[] memory, uint256[] memory); function getOptimalWithdrawalPath(address gauge, uint256 amount) external view returns (address[] memory, uint256[] memory); function getFallbacks(address gauge) external view returns (address[] memory); } // src/optimizer/Optimizer.sol /// @title Optimizer /// @author Stake DAO /// @notice Module to compute optimal allocation between Stake DAO and Convex. /// @dev It should inherit from IOnlyBoost to be used in the StakeDAO Strategy. contract Optimizer is IOnlyBoost { using FixedPointMathLib for uint256; /// @notice Struct to store cached optimization values and timestamp /// @param value Cached optimization value /// @param timestamp Timestamp of the cached optimization struct CachedOptimization { uint256 value; uint256 timestamp; } /// @notice Stake DAO Curve Strategy address public immutable strategy; /// @notice Minimal Proxy Factory for Convex Deposit Contracts. IConvexFactory public immutable proxyFactory; /// @notice StakeDAO CRV Locker. address public constant VOTER_PROXY_SD = 0x52f541764E6e90eeBc5c21Ff570De0e2D63766B6; /// @notice Convex CRV Locker. address public constant VOTER_PROXY_CONVEX = 0x989AEb4d175e16225E39E87d0D97A3360524AD80; /// @notice Boost Delegation V3 contract. address public constant BOOST_DELEGATION_V3 = 0xD37A6aa3d8460Bd2b6536d608103D880695A23CD; /// @notice Address of the governance. address public governance; /// @notice Address of the future governance contract. address public futureGovernance; /// @notice Cache period for optimization. uint256 public cachePeriod = 7 days; /// @notice Map gauge => CachedOptimization. mapping(address => CachedOptimization) public cachedOptimizations; //////////////////////////////////////////////////////////////// /// --- EVENTS & ERRORS /////////////////////////////////////////////////////////////// /// @notice Event emitted when governance is changed. /// @param newGovernance Address of the new governance. event GovernanceChanged(address indexed newGovernance); /// @notice Error emitted when auth failed error GOVERNANCE(); /// @notice Error emitted when the caller is not the strategy. error STRATEGY(); modifier onlyGovernance() { if (msg.sender != governance) revert GOVERNANCE(); _; } modifier onlyStrategy() { if (msg.sender != strategy) revert STRATEGY(); _; } /// @notice Constructor to set the curve strategy and proxy factory /// @param _curveStrategy The address of the curve strategy /// @param _proxyFactory The address of the proxy factory constructor(address _curveStrategy, address _proxyFactory) { governance = msg.sender; strategy = _curveStrategy; proxyFactory = IConvexFactory(_proxyFactory); } ////////////////////////////////////////////////////// /// --- OPTIMIZATION FOR STAKEDAO ////////////////////////////////////////////////////// /// @notice Return the optimal amount of LP token that must be held by Stake DAO Liquidity Locker /// @param gauge Address of the gauge. /// @return balanceSD Optimal amount of LP token function computeOptimalDepositAmount(address gauge) public view returns (uint256 balanceSD) { // 1. Get the balance of veBoost on Stake DAO and Convex uint256 veBoostSD = ERC20(BOOST_DELEGATION_V3).balanceOf(VOTER_PROXY_SD); uint256 veBoostConvex = ERC20(BOOST_DELEGATION_V3).balanceOf(VOTER_PROXY_CONVEX); // 2. Get the balance of the liquidity gauge on Convex uint256 balanceConvex = ERC20(gauge).balanceOf(VOTER_PROXY_CONVEX); // 3. Compute the optimal balance for Stake DAO if (balanceConvex != 0) { balanceSD = balanceConvex.mulDiv(veBoostSD, veBoostConvex); } } ////////////////////////////////////////////////////// /// --- OPTIMIZATION FOR STRATEGY DEPOSIT & WITHDRAW ////////////////////////////////////////////////////// /// @notice Return the amount that need to be deposited StakeDAO Liquid Locker and on each fallback /// @dev This is not a view due to the cache system /// @param gauge Address of Liquidity Gauge corresponding to LP token /// @param amount Amount of LP token to deposit function getOptimalDepositAllocation(address gauge, uint256 amount) public onlyStrategy returns (address[] memory _depositors, uint256[] memory _allocations) { /// Gets the fallback address via the proxy factory; one fallback (clone) per Convex pid address _fallback = proxyFactory.fallbacks(gauge); // If available on Convex Curve if (_fallback != address(0)) { /// Initialize arrays _depositors = new address[](2); _allocations = new uint256[](2); _depositors[0] = _fallback; _depositors[1] = VOTER_PROXY_SD; // If Convex Curve has max boost, no need to optimize if ( ILiquidityGauge(gauge).working_balances(VOTER_PROXY_CONVEX) == ERC20(gauge).balanceOf(VOTER_PROXY_CONVEX) ) { _allocations[0] = amount; } else { // Get the balance of the locker on the liquidity gauge uint256 gaugeBalance = ERC20(gauge).balanceOf(address(VOTER_PROXY_SD)); // Get the optimal amount of lps that must be held by the locker uint256 opt = _getOptimalAmount(gauge, gaugeBalance, amount); // Stake DAO Curve _allocations[1] = opt > gaugeBalance ? FixedPointMathLib.min(opt - gaugeBalance, amount) : 0; // Convex Curve _allocations[0] = amount - _allocations[1]; } } // If not available on Convex // We only deposit on Stake DAO else { /// Initialize arrays _depositors = new address[](1); _depositors[0] = VOTER_PROXY_SD; _allocations = new uint256[](1); _allocations[0] = amount; } } /// @notice Return the amount that need to be deposited StakeDAO Liquid Locker and on each fallback /// @dev This is not a view due to the cache system /// @param gauge Address of Liquidity Gauge corresponding to LP token /// @param amount Amount of LP token to deposit function getRebalancedAllocation(address gauge, uint256 amount) public onlyStrategy returns (address[] memory _depositors, uint256[] memory _allocations) { /// Gets the fallback address via the proxy factory; one fallback (clone) per Convex pid address _fallback = proxyFactory.fallbacks(gauge); // If available on Convex Curve if (_fallback != address(0)) { /// Initialize arrays _depositors = new address[](2); _allocations = new uint256[](2); _depositors[0] = _fallback; _depositors[1] = VOTER_PROXY_SD; // Calculate optimal amount uint256 opt = computeOptimalDepositAmount(gauge); // Cache only if needed if (cachePeriod != 0) { // Update the cache for Classic Pool cachedOptimizations[gauge] = CachedOptimization(opt, block.timestamp); } // Stake DAO Curve _allocations[1] = amount > opt ? opt : amount; // Convex Curve _allocations[0] = amount - _allocations[1]; } else { /// Initialize arrays _depositors = new address[](1); _depositors[0] = VOTER_PROXY_SD; _allocations = new uint256[](1); _allocations[0] = amount; } } /// @notice Calcul the optimal amount of lps that must be held by the locker or use the cached value /// @param gauge Address of the liquidity gauge /// @param gaugeBalance Balance of the liquidity gauge on Convex Curve /// @param amount Amount of LP token to get the optimal amount for /// @return opt Optimal amount of LP token that must be held by the locker function _getOptimalAmount(address gauge, uint256 gaugeBalance, uint256 amount) internal returns (uint256 opt) { CachedOptimization memory cachedOptimization = cachedOptimizations[gauge]; if ( /// If the cache is enabled /// And the new deposit is lower than the cached optimal amount cachedOptimization.timestamp + cachePeriod > block.timestamp && cachedOptimization.value >= amount + gaugeBalance ) { // Use cached optimal amount return cachedOptimization.value; } else { // Calculate optimal amount opt = computeOptimalDepositAmount(gauge); // Cache only if needed if (cachePeriod != 0) { // Update the cache for Classic Pool cachedOptimizations[gauge] = CachedOptimization(opt, block.timestamp); } } } /// @notice Return the amount that need to be withdrawn from StakeDAO Liquid Locker and from Convex Curve based on the amount to withdraw and boost optimization /// @param gauge Address of Liquidity Gauge corresponding to LP token /// @param amount Amount of LP token to withdraw function getOptimalWithdrawalPath(address gauge, uint256 amount) public view returns (address[] memory _withdrawalTargets, uint256[] memory _allocations) { /// Gets the fallback address via the proxy factory; one fallback (clone) per Convex pid address _fallback = proxyFactory.fallbacks(gauge); if (_fallback != address(0)) { _withdrawalTargets = new address[](2); _allocations = new uint256[](2); _withdrawalTargets[1] = VOTER_PROXY_SD; _withdrawalTargets[0] = _fallback; uint256 balanceSD = ERC20(gauge).balanceOf(VOTER_PROXY_SD); uint256 balanceConvex = IFallback(_fallback).balanceOf(gauge); // Calculate optimal boost for both pools uint256 optimalSD = cachedOptimizations[gauge].value; uint256 totalBalance = balanceSD + balanceConvex; // Adjust the withdrawal based on the optimal amount for Stake DAO if (totalBalance <= amount) { // If the total balance is less than or equal to the withdrawal amount, withdraw everything _allocations[0] = balanceConvex; _allocations[1] = balanceSD; } else if (optimalSD >= balanceSD) { // If Stake DAO balance is below optimal, prioritize withdrawing from Convex _allocations[0] = FixedPointMathLib.min(amount, balanceConvex); _allocations[1] = amount > _allocations[0] ? amount - _allocations[0] : 0; } else { // If Stake DAO balance is above optimal, prioritize withdrawing from Stake DAO _allocations[1] = FixedPointMathLib.min(amount, balanceSD); _allocations[0] = amount > _allocations[1] ? amount - _allocations[1] : 0; } } else { _withdrawalTargets = new address[](1); _withdrawalTargets[0] = VOTER_PROXY_SD; _allocations = new uint256[](1); _allocations[0] = amount; } } /// @notice Get the fallbacks address for a gauge /// @param gauge Address of the gauge function getFallbacks(address gauge) public view returns (address[] memory _fallbacks) { _fallbacks = new address[](1); _fallbacks[0] = address(proxyFactory.fallbacks(gauge)); } /// @notice Set the cache period /// @param newCachePeriod New cache period /// @dev Only the governance can call this function setCachePeriod(uint256 newCachePeriod) external onlyGovernance { cachePeriod = newCachePeriod; } /// @notice Transfer the governance to a new address. /// @param _governance Address of the new governance. /// @dev Only the governance can call this /// @dev 2 step process, first you call this function with the new governance address, then the new governance contract has to call `acceptGovernance` function transferGovernance(address _governance) external onlyGovernance { futureGovernance = _governance; } /// @notice Accept the governance transfer. function acceptGovernance() external { if (msg.sender != futureGovernance) revert GOVERNANCE(); governance = msg.sender; futureGovernance = address(0); emit GovernanceChanged(msg.sender); } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_curveStrategy","type":"address"},{"internalType":"address","name":"_proxyFactory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"GOVERNANCE","type":"error"},{"inputs":[],"name":"STRATEGY","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newGovernance","type":"address"}],"name":"GovernanceChanged","type":"event"},{"inputs":[],"name":"BOOST_DELEGATION_V3","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VOTER_PROXY_CONVEX","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VOTER_PROXY_SD","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cachePeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"cachedOptimizations","outputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"}],"name":"computeOptimalDepositAmount","outputs":[{"internalType":"uint256","name":"balanceSD","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"futureGovernance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"}],"name":"getFallbacks","outputs":[{"internalType":"address[]","name":"_fallbacks","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getOptimalDepositAllocation","outputs":[{"internalType":"address[]","name":"_depositors","type":"address[]"},{"internalType":"uint256[]","name":"_allocations","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getOptimalWithdrawalPath","outputs":[{"internalType":"address[]","name":"_withdrawalTargets","type":"address[]"},{"internalType":"uint256[]","name":"_allocations","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getRebalancedAllocation","outputs":[{"internalType":"address[]","name":"_depositors","type":"address[]"},{"internalType":"uint256[]","name":"_allocations","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxyFactory","outputs":[{"internalType":"contract IConvexFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newCachePeriod","type":"uint256"}],"name":"setCachePeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"strategy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_governance","type":"address"}],"name":"transferGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60c060405262093a8060025534801561001757600080fd5b506040516115213803806115218339810160408190526100369161007b565b600080546001600160a01b031916331790556001600160a01b039182166080521660a0526100ae565b80516001600160a01b038116811461007657600080fd5b919050565b6000806040838503121561008e57600080fd5b6100978361005f565b91506100a56020840161005f565b90509250929050565b60805160a0516114246100fd6000396000818161028c0152818161035d0152818161062f01528181610a9f0152610be2015260008181610265015281816102fd0152610b8201526114246000f3fe608060405234801561001057600080fd5b506004361061010b5760003560e01c8063658c0593116100a2578063a8c62e7611610071578063a8c62e7614610260578063c10f1a7514610287578063d38bfff4146102ae578063ee0316d6146102c1578063f7bbd4ec146102dc57600080fd5b8063658c05931461021057806365c138a4146102235780638070c50314610236578063a741f5291461024957600080fd5b8063238efcbc116100de578063238efcbc146101b85780632f02a102146101c257806330a1e476146101dd5780635aa6e675146101fd57600080fd5b80630a599a1314610110578063131983c41461015157806318bde03114610184578063237bc613146101a5575b600080fd5b61013761011e366004611250565b6003602052600090815260409020805460019091015482565b604080519283526020830191909152015b60405180910390f35b61016c73989aeb4d175e16225e39e87d0d97a3360524ad8081565b6040516001600160a01b039091168152602001610148565b61019761019236600461126d565b6102ef565b6040516101489291906112dd565b6101976101b336600461126d565b610608565b6101c06109e9565b005b61016c73d37a6aa3d8460bd2b6536d608103d880695a23cd81565b6101f06101eb366004611250565b610a5d565b6040516101489190611334565b60005461016c906001600160a01b031681565b6101c061021e366004611347565b610b44565b61019761023136600461126d565b610b74565b60015461016c906001600160a01b031681565b61025260025481565b604051908152602001610148565b61016c7f000000000000000000000000000000000000000000000000000000000000000081565b61016c7f000000000000000000000000000000000000000000000000000000000000000081565b6101c06102bc366004611250565b610f4e565b61016c7352f541764e6e90eebc5c21ff570de0e2d63766b681565b6102526102ea366004611250565b610f9b565b606080336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461033b5760405163185025ef60e01b815260040160405180910390fd5b6040516326256c0b60e01b81526001600160a01b0385811660048301526000917f0000000000000000000000000000000000000000000000000000000000000000909116906326256c0b90602401602060405180830381865afa1580156103a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ca9190611360565b90506001600160a01b038116156105615760408051600280825260608201835290916020830190803683375050604080516002808252606082018352939650929150602083019080368337019050509150808360008151811061042f5761042f61137d565b60200260200101906001600160a01b031690816001600160a01b0316815250507352f541764e6e90eebc5c21ff570de0e2d63766b6836001815181106104775761047761137d565b60200260200101906001600160a01b031690816001600160a01b03168152505060006104a286610f9b565b90506002546000146104e8576040805180820182528281524260208083019182526001600160a01b038a1660009081526003909152929092209051815590516001909101555b8085116104f557846104f7565b805b8360018151811061050a5761050a61137d565b602002602001018181525050826001815181106105295761052961137d565b60200260200101518561053c91906113a9565b8360008151811061054f5761054f61137d565b60200260200101818152505050610600565b60408051600180825281830190925290602080830190803683370190505092507352f541764e6e90eebc5c21ff570de0e2d63766b6836000815181106105a9576105a961137d565b6001600160a01b039290921660209283029190910182015260408051600180825281830190925291828101908036833701905050915083826000815181106105f3576105f361137d565b6020026020010181815250505b509250929050565b6040516326256c0b60e01b81526001600160a01b03838116600483015260609182916000917f0000000000000000000000000000000000000000000000000000000000000000909116906326256c0b90602401602060405180830381865afa158015610678573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061069c9190611360565b90506001600160a01b0381161561056157604080516002808252606082018352909160208301908036833750506040805160028082526060820183529396509291506020830190803683370190505091507352f541764e6e90eebc5c21ff570de0e2d63766b6836001815181106107155761071561137d565b60200260200101906001600160a01b031690816001600160a01b03168152505080836000815181106107495761074961137d565b6001600160a01b0392831660209182029290920101526040516370a0823160e01b81527352f541764e6e90eebc5c21ff570de0e2d63766b660048201526000918716906370a0823190602401602060405180830381865afa1580156107b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d691906113c2565b6040516370a0823160e01b81526001600160a01b0388811660048301529192506000918416906370a0823190602401602060405180830381865afa158015610822573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061084691906113c2565b6001600160a01b03881660009081526003602052604081205491925061086c83856113db565b90508781116108ba5782866000815181106108895761088961137d565b60200260200101818152505083866001815181106108a9576108a961137d565b6020026020010181815250506109e0565b83821061094a57828811838918028818866000815181106108dd576108dd61137d565b602002602001018181525050856000815181106108fc576108fc61137d565b60200260200101518811610911576000610937565b856000815181106109245761092461137d565b60200260200101518861093791906113a9565b866001815181106108a9576108a961137d565b838811848918028818866001815181106109665761096661137d565b602002602001018181525050856001815181106109855761098561137d565b6020026020010151881161099a5760006109c0565b856001815181106109ad576109ad61137d565b6020026020010151886109c091906113a9565b866000815181106109d3576109d361137d565b6020026020010181815250505b50505050610600565b6001546001600160a01b03163314610a14576040516305189e0d60e21b815260040160405180910390fd5b60008054336001600160a01b0319918216811783556001805490921690915560405190917fa6a85f15b976d399f39ad43e515e75910bac714bc55eeff6131fb90780d6f74691a2565b60408051600180825281830190925260609160208083019080368337019050506040516326256c0b60e01b81526001600160a01b0384811660048301529192507f0000000000000000000000000000000000000000000000000000000000000000909116906326256c0b90602401602060405180830381865afa158015610ae8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0c9190611360565b81600081518110610b1f57610b1f61137d565b60200260200101906001600160a01b031690816001600160a01b031681525050919050565b6000546001600160a01b03163314610b6f576040516305189e0d60e21b815260040160405180910390fd5b600255565b606080336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610bc05760405163185025ef60e01b815260040160405180910390fd5b6040516326256c0b60e01b81526001600160a01b0385811660048301526000917f0000000000000000000000000000000000000000000000000000000000000000909116906326256c0b90602401602060405180830381865afa158015610c2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4f9190611360565b90506001600160a01b0381161561056157604080516002808252606082018352909160208301908036833750506040805160028082526060820183529396509291506020830190803683370190505091508083600081518110610cb457610cb461137d565b60200260200101906001600160a01b031690816001600160a01b0316815250507352f541764e6e90eebc5c21ff570de0e2d63766b683600181518110610cfc57610cfc61137d565b6001600160a01b0392831660209182029290920101526040516370a0823160e01b815273989aeb4d175e16225e39e87d0d97a3360524ad806004820152908616906370a0823190602401602060405180830381865afa158015610d63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d8791906113c2565b6040516309f658e560e11b815273989aeb4d175e16225e39e87d0d97a3360524ad8060048201526001600160a01b038716906313ecb1ca90602401602060405180830381865afa158015610ddf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e0391906113c2565b03610e2d578382600081518110610e1c57610e1c61137d565b602002602001018181525050610600565b6040516370a0823160e01b81527352f541764e6e90eebc5c21ff570de0e2d63766b660048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015610e88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eac91906113c2565b90506000610ebb878388611154565b9050818111610ecb576000610ee3565b610ee3610ed883836113a9565b878111818918021890565b84600181518110610ef657610ef661137d565b60200260200101818152505083600181518110610f1557610f1561137d565b602002602001015186610f2891906113a9565b84600081518110610f3b57610f3b61137d565b6020026020010181815250505050610600565b6000546001600160a01b03163314610f79576040516305189e0d60e21b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6040516370a0823160e01b81527352f541764e6e90eebc5c21ff570de0e2d63766b66004820152600090819073d37a6aa3d8460bd2b6536d608103d880695a23cd906370a0823190602401602060405180830381865afa158015611003573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061102791906113c2565b6040516370a0823160e01b815273989aeb4d175e16225e39e87d0d97a3360524ad80600482015290915060009073d37a6aa3d8460bd2b6536d608103d880695a23cd906370a0823190602401602060405180830381865afa158015611090573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110b491906113c2565b6040516370a0823160e01b815273989aeb4d175e16225e39e87d0d97a3360524ad8060048201529091506000906001600160a01b038616906370a0823190602401602060405180830381865afa158015611112573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113691906113c2565b9050801561114c57611149818484611211565b93505b505050919050565b6001600160a01b03831660009081526003602090815260408083208151808301909252805482526001015491810182905260025490914291611195916113db565b1180156111ac57506111a784846113db565b815110155b156111b95751905061120a565b6111c285610f9b565b9150600254600014611208576040805180820182528381524260208083019182526001600160a01b03891660009081526003909152929092209051815590516001909101555b505b9392505050565b60008260001904841183021582026112315763ad251c276000526004601cfd5b5091020490565b6001600160a01b038116811461124d57600080fd5b50565b60006020828403121561126257600080fd5b813561120a81611238565b6000806040838503121561128057600080fd5b823561128b81611238565b946020939093013593505050565b600081518084526020808501945080840160005b838110156112d25781516001600160a01b0316875295820195908201906001016112ad565b509495945050505050565b6040815260006112f06040830185611299565b82810360208481019190915284518083528582019282019060005b818110156113275784518352938301939183019160010161130b565b5090979650505050505050565b60208152600061120a6020830184611299565b60006020828403121561135957600080fd5b5035919050565b60006020828403121561137257600080fd5b815161120a81611238565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b818103818111156113bc576113bc611393565b92915050565b6000602082840312156113d457600080fd5b5051919050565b808201808211156113bc576113bc61139356fea26469706673582212208472621cc88d0b1c814e6c42ec9459e8966e9586475b3b12de770e9dd7f3f94164736f6c6343000813003300000000000000000000000069d61428d089c2f35bf6a472f540d0f82d1ea2cd0000000000000000000000004e795a6f991e305e3f28a3b1b2b4b9789d2cd5a1
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061010b5760003560e01c8063658c0593116100a2578063a8c62e7611610071578063a8c62e7614610260578063c10f1a7514610287578063d38bfff4146102ae578063ee0316d6146102c1578063f7bbd4ec146102dc57600080fd5b8063658c05931461021057806365c138a4146102235780638070c50314610236578063a741f5291461024957600080fd5b8063238efcbc116100de578063238efcbc146101b85780632f02a102146101c257806330a1e476146101dd5780635aa6e675146101fd57600080fd5b80630a599a1314610110578063131983c41461015157806318bde03114610184578063237bc613146101a5575b600080fd5b61013761011e366004611250565b6003602052600090815260409020805460019091015482565b604080519283526020830191909152015b60405180910390f35b61016c73989aeb4d175e16225e39e87d0d97a3360524ad8081565b6040516001600160a01b039091168152602001610148565b61019761019236600461126d565b6102ef565b6040516101489291906112dd565b6101976101b336600461126d565b610608565b6101c06109e9565b005b61016c73d37a6aa3d8460bd2b6536d608103d880695a23cd81565b6101f06101eb366004611250565b610a5d565b6040516101489190611334565b60005461016c906001600160a01b031681565b6101c061021e366004611347565b610b44565b61019761023136600461126d565b610b74565b60015461016c906001600160a01b031681565b61025260025481565b604051908152602001610148565b61016c7f00000000000000000000000069d61428d089c2f35bf6a472f540d0f82d1ea2cd81565b61016c7f0000000000000000000000004e795a6f991e305e3f28a3b1b2b4b9789d2cd5a181565b6101c06102bc366004611250565b610f4e565b61016c7352f541764e6e90eebc5c21ff570de0e2d63766b681565b6102526102ea366004611250565b610f9b565b606080336001600160a01b037f00000000000000000000000069d61428d089c2f35bf6a472f540d0f82d1ea2cd161461033b5760405163185025ef60e01b815260040160405180910390fd5b6040516326256c0b60e01b81526001600160a01b0385811660048301526000917f0000000000000000000000004e795a6f991e305e3f28a3b1b2b4b9789d2cd5a1909116906326256c0b90602401602060405180830381865afa1580156103a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ca9190611360565b90506001600160a01b038116156105615760408051600280825260608201835290916020830190803683375050604080516002808252606082018352939650929150602083019080368337019050509150808360008151811061042f5761042f61137d565b60200260200101906001600160a01b031690816001600160a01b0316815250507352f541764e6e90eebc5c21ff570de0e2d63766b6836001815181106104775761047761137d565b60200260200101906001600160a01b031690816001600160a01b03168152505060006104a286610f9b565b90506002546000146104e8576040805180820182528281524260208083019182526001600160a01b038a1660009081526003909152929092209051815590516001909101555b8085116104f557846104f7565b805b8360018151811061050a5761050a61137d565b602002602001018181525050826001815181106105295761052961137d565b60200260200101518561053c91906113a9565b8360008151811061054f5761054f61137d565b60200260200101818152505050610600565b60408051600180825281830190925290602080830190803683370190505092507352f541764e6e90eebc5c21ff570de0e2d63766b6836000815181106105a9576105a961137d565b6001600160a01b039290921660209283029190910182015260408051600180825281830190925291828101908036833701905050915083826000815181106105f3576105f361137d565b6020026020010181815250505b509250929050565b6040516326256c0b60e01b81526001600160a01b03838116600483015260609182916000917f0000000000000000000000004e795a6f991e305e3f28a3b1b2b4b9789d2cd5a1909116906326256c0b90602401602060405180830381865afa158015610678573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061069c9190611360565b90506001600160a01b0381161561056157604080516002808252606082018352909160208301908036833750506040805160028082526060820183529396509291506020830190803683370190505091507352f541764e6e90eebc5c21ff570de0e2d63766b6836001815181106107155761071561137d565b60200260200101906001600160a01b031690816001600160a01b03168152505080836000815181106107495761074961137d565b6001600160a01b0392831660209182029290920101526040516370a0823160e01b81527352f541764e6e90eebc5c21ff570de0e2d63766b660048201526000918716906370a0823190602401602060405180830381865afa1580156107b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d691906113c2565b6040516370a0823160e01b81526001600160a01b0388811660048301529192506000918416906370a0823190602401602060405180830381865afa158015610822573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061084691906113c2565b6001600160a01b03881660009081526003602052604081205491925061086c83856113db565b90508781116108ba5782866000815181106108895761088961137d565b60200260200101818152505083866001815181106108a9576108a961137d565b6020026020010181815250506109e0565b83821061094a57828811838918028818866000815181106108dd576108dd61137d565b602002602001018181525050856000815181106108fc576108fc61137d565b60200260200101518811610911576000610937565b856000815181106109245761092461137d565b60200260200101518861093791906113a9565b866001815181106108a9576108a961137d565b838811848918028818866001815181106109665761096661137d565b602002602001018181525050856001815181106109855761098561137d565b6020026020010151881161099a5760006109c0565b856001815181106109ad576109ad61137d565b6020026020010151886109c091906113a9565b866000815181106109d3576109d361137d565b6020026020010181815250505b50505050610600565b6001546001600160a01b03163314610a14576040516305189e0d60e21b815260040160405180910390fd5b60008054336001600160a01b0319918216811783556001805490921690915560405190917fa6a85f15b976d399f39ad43e515e75910bac714bc55eeff6131fb90780d6f74691a2565b60408051600180825281830190925260609160208083019080368337019050506040516326256c0b60e01b81526001600160a01b0384811660048301529192507f0000000000000000000000004e795a6f991e305e3f28a3b1b2b4b9789d2cd5a1909116906326256c0b90602401602060405180830381865afa158015610ae8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0c9190611360565b81600081518110610b1f57610b1f61137d565b60200260200101906001600160a01b031690816001600160a01b031681525050919050565b6000546001600160a01b03163314610b6f576040516305189e0d60e21b815260040160405180910390fd5b600255565b606080336001600160a01b037f00000000000000000000000069d61428d089c2f35bf6a472f540d0f82d1ea2cd1614610bc05760405163185025ef60e01b815260040160405180910390fd5b6040516326256c0b60e01b81526001600160a01b0385811660048301526000917f0000000000000000000000004e795a6f991e305e3f28a3b1b2b4b9789d2cd5a1909116906326256c0b90602401602060405180830381865afa158015610c2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4f9190611360565b90506001600160a01b0381161561056157604080516002808252606082018352909160208301908036833750506040805160028082526060820183529396509291506020830190803683370190505091508083600081518110610cb457610cb461137d565b60200260200101906001600160a01b031690816001600160a01b0316815250507352f541764e6e90eebc5c21ff570de0e2d63766b683600181518110610cfc57610cfc61137d565b6001600160a01b0392831660209182029290920101526040516370a0823160e01b815273989aeb4d175e16225e39e87d0d97a3360524ad806004820152908616906370a0823190602401602060405180830381865afa158015610d63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d8791906113c2565b6040516309f658e560e11b815273989aeb4d175e16225e39e87d0d97a3360524ad8060048201526001600160a01b038716906313ecb1ca90602401602060405180830381865afa158015610ddf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e0391906113c2565b03610e2d578382600081518110610e1c57610e1c61137d565b602002602001018181525050610600565b6040516370a0823160e01b81527352f541764e6e90eebc5c21ff570de0e2d63766b660048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015610e88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eac91906113c2565b90506000610ebb878388611154565b9050818111610ecb576000610ee3565b610ee3610ed883836113a9565b878111818918021890565b84600181518110610ef657610ef661137d565b60200260200101818152505083600181518110610f1557610f1561137d565b602002602001015186610f2891906113a9565b84600081518110610f3b57610f3b61137d565b6020026020010181815250505050610600565b6000546001600160a01b03163314610f79576040516305189e0d60e21b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6040516370a0823160e01b81527352f541764e6e90eebc5c21ff570de0e2d63766b66004820152600090819073d37a6aa3d8460bd2b6536d608103d880695a23cd906370a0823190602401602060405180830381865afa158015611003573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061102791906113c2565b6040516370a0823160e01b815273989aeb4d175e16225e39e87d0d97a3360524ad80600482015290915060009073d37a6aa3d8460bd2b6536d608103d880695a23cd906370a0823190602401602060405180830381865afa158015611090573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110b491906113c2565b6040516370a0823160e01b815273989aeb4d175e16225e39e87d0d97a3360524ad8060048201529091506000906001600160a01b038616906370a0823190602401602060405180830381865afa158015611112573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113691906113c2565b9050801561114c57611149818484611211565b93505b505050919050565b6001600160a01b03831660009081526003602090815260408083208151808301909252805482526001015491810182905260025490914291611195916113db565b1180156111ac57506111a784846113db565b815110155b156111b95751905061120a565b6111c285610f9b565b9150600254600014611208576040805180820182528381524260208083019182526001600160a01b03891660009081526003909152929092209051815590516001909101555b505b9392505050565b60008260001904841183021582026112315763ad251c276000526004601cfd5b5091020490565b6001600160a01b038116811461124d57600080fd5b50565b60006020828403121561126257600080fd5b813561120a81611238565b6000806040838503121561128057600080fd5b823561128b81611238565b946020939093013593505050565b600081518084526020808501945080840160005b838110156112d25781516001600160a01b0316875295820195908201906001016112ad565b509495945050505050565b6040815260006112f06040830185611299565b82810360208481019190915284518083528582019282019060005b818110156113275784518352938301939183019160010161130b565b5090979650505050505050565b60208152600061120a6020830184611299565b60006020828403121561135957600080fd5b5035919050565b60006020828403121561137257600080fd5b815161120a81611238565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b818103818111156113bc576113bc611393565b92915050565b6000602082840312156113d457600080fd5b5051919050565b808201808211156113bc576113bc61139356fea26469706673582212208472621cc88d0b1c814e6c42ec9459e8966e9586475b3b12de770e9dd7f3f94164736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000069d61428d089c2f35bf6a472f540d0f82d1ea2cd0000000000000000000000004e795a6f991e305e3f28a3b1b2b4b9789d2cd5a1
-----Decoded View---------------
Arg [0] : _curveStrategy (address): 0x69D61428d089C2F35Bf6a472F540D0F82D1EA2cd
Arg [1] : _proxyFactory (address): 0x4E795A6f991e305e3f28A3b1b2B4B9789d2CD5A1
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000069d61428d089c2f35bf6a472f540d0f82d1ea2cd
Arg [1] : 0000000000000000000000004e795a6f991e305e3f28a3b1b2b4b9789d2cd5a1
Deployed Bytecode Sourcemap
75937:12663:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;77232:65;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;576:25:1;;;632:2;617:18;;610:34;;;;549:18;77232:65:0;;;;;;;;76672:87;;76717:42;76672:87;;;;;-1:-1:-1;;;;;819:32:1;;;801:51;;789:2;774:18;76672:87:0;655:203:1;82132:1413:0;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;:::i;85183:2104::-;;;;;;:::i;:::-;;:::i;88363:234::-;;;:::i;:::-;;76815:88;;76861:42;76815:88;;87393:200;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;76956:25::-;;;;;-1:-1:-1;;;;;76956:25:0;;;87735:119;;;;;;:::i;:::-;;:::i;79951:1883::-;;;;;;:::i;:::-;;:::i;77050:31::-;;;;;-1:-1:-1;;;;;77050:31:0;;;77138:35;;;;;;;;;3055:25:1;;;3043:2;3028:18;77138:35:0;2909:177:1;76342:33:0;;;;;76453:44;;;;;88184:122;;;;;;:::i;:::-;;:::i;76544:83::-;;76585:42;76544:83;;78812:661;;;;;;:::i;:::-;;:::i;82132:1413::-;82252:28;;77980:10;-1:-1:-1;;;;;77994:8:0;77980:22;;77976:45;;78011:10;;-1:-1:-1;;;78011:10:0;;;;;;;;;;;77976:45;82447:29:::1;::::0;-1:-1:-1;;;82447:29:0;;-1:-1:-1;;;;;819:32:1;;;82447:29:0::1;::::0;::::1;801:51:1::0;82427:17:0::1;::::0;82447:12:::1;:22:::0;;::::1;::::0;::::1;::::0;774:18:1;;82447:29:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;82427:49:::0;-1:-1:-1;;;;;;82534:23:0;::::1;::::0;82530:1008:::1;;82623:16;::::0;;82637:1:::1;82623:16:::0;;;;;::::1;::::0;;;;::::1;::::0;::::1;::::0;;::::1;::::0;::::1;-1:-1:-1::0;;82669:16:0::1;::::0;;82683:1:::1;82669:16:::0;;;;;::::1;::::0;;82609:30;;-1:-1:-1;82669:16:0;82683:1;-1:-1:-1;82669:16:0::1;::::0;::::1;::::0;;::::1;::::0;::::1;;::::0;-1:-1:-1;82669:16:0::1;82654:31;;82719:9;82702:11;82714:1;82702:14;;;;;;;;:::i;:::-;;;;;;:26;-1:-1:-1::0;;;;;82702:26:0::1;;;-1:-1:-1::0;;;;;82702:26:0::1;;;::::0;::::1;76585:42;82743:11;82755:1;82743:14;;;;;;;;:::i;:::-;;;;;;:31;-1:-1:-1::0;;;;;82743:31:0::1;;;-1:-1:-1::0;;;;;82743:31:0::1;;;::::0;::::1;82832:11;82846:34;82874:5;82846:27;:34::i;:::-;82832:48;;82938:11;;82953:1;82938:16;82934:180;;83058:40;::::0;;;;::::1;::::0;;;;;83082:15:::1;83058:40;::::0;;::::1;::::0;;;-1:-1:-1;;;;;83029:26:0;::::1;-1:-1:-1::0;83029:26:0;;;:19:::1;:26:::0;;;;;;;:69;;;;;;::::1;::::0;;::::1;::::0;82934:180:::1;83189:3;83180:6;:12;:27;;83201:6;83180:27;;;83195:3;83180:27;83162:12;83175:1;83162:15;;;;;;;;:::i;:::-;;;;;;:45;;;::::0;::::1;83280:12;83293:1;83280:15;;;;;;;;:::i;:::-;;;;;;;83271:6;:24;;;;:::i;:::-;83253:12;83266:1;83253:15;;;;;;;;:::i;:::-;;;;;;:42;;;::::0;::::1;82559:748;82530:1008;;;83377:16;::::0;;83391:1:::1;83377:16:::0;;;;;::::1;::::0;;;;::::1;::::0;;::::1;::::0;;::::1;::::0;::::1;;::::0;-1:-1:-1;83377:16:0::1;83363:30;;76585:42;83408:11;83420:1;83408:14;;;;;;;;:::i;:::-;-1:-1:-1::0;;;;;83408:31:0;;;::::1;:14;::::0;;::::1;::::0;;;;;;:31;83471:16:::1;::::0;;83485:1:::1;83471:16:::0;;;;;::::1;::::0;;;;;;::::1;::::0;83408:14;83471:16:::1;::::0;::::1;;::::0;-1:-1:-1;83471:16:0::1;83456:31;;83520:6;83502:12;83515:1;83502:15;;;;;;;;:::i;:::-;;;;;;:24;;;::::0;::::1;82530:1008;82318:1227;82132:1413:::0;;;;;:::o;85183:2104::-;85498:29;;-1:-1:-1;;;85498:29:0;;-1:-1:-1;;;;;819:32:1;;;85498:29:0;;;801:51:1;85296:35:0;;;;-1:-1:-1;;85498:12:0;:22;;;;;;774:18:1;;85498:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;85478:49;-1:-1:-1;;;;;;85544:23:0;;;85540:1740;;85605:16;;;85619:1;85605:16;;;;;;;;;;;;;;;;;;-1:-1:-1;;85651:16:0;;;85665:1;85651:16;;;;;;;;85584:37;;-1:-1:-1;85651:16:0;85665:1;-1:-1:-1;85651:16:0;;;;;;;;;;-1:-1:-1;85651:16:0;85636:31;;76585:42;85684:18;85703:1;85684:21;;;;;;;;:::i;:::-;;;;;;:38;-1:-1:-1;;;;;85684:38:0;;;-1:-1:-1;;;;;85684:38:0;;;;;85761:9;85737:18;85756:1;85737:21;;;;;;;;:::i;:::-;-1:-1:-1;;;;;85737:33:0;;;:21;;;;;;;;;:33;85807:38;;-1:-1:-1;;;85807:38:0;;76585:42;85807:38;;;801:51:1;85787:17:0;;85807:22;;;;;774:18:1;;85807:38:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;85884:37;;-1:-1:-1;;;85884:37:0;;-1:-1:-1;;;;;819:32:1;;;85884:37:0;;;801:51:1;85787:58:0;;-1:-1:-1;85860:21:0;;85884:30;;;;;774:18:1;;85884:37:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;86013:26:0;;85993:17;86013:26;;;:19;:26;;;;;:32;85860:61;;-1:-1:-1;86083:25:0;85860:61;86083:9;:25;:::i;:::-;86060:48;;86225:6;86209:12;:22;86205:854;;86379:13;86361:12;86374:1;86361:15;;;;;;;;:::i;:::-;;;;;;:31;;;;;86429:9;86411:12;86424:1;86411:15;;;;;;;;:::i;:::-;;;;;;:27;;;;;86205:854;;;86477:9;86464;:22;86460:599;;-1:-1:-1;;;67505:9:0;;;67501:24;67494:32;;86601:12;86614:1;86601:15;;;;;;;;:::i;:::-;;;;;;:62;;;;;86709:12;86722:1;86709:15;;;;;;;;:::i;:::-;;;;;;;86700:6;:24;:55;;86754:1;86700:55;;;86736:12;86749:1;86736:15;;;;;;;;:::i;:::-;;;;;;;86727:6;:24;;;;:::i;:::-;86682:12;86695:1;86682:15;;;;;;;;:::i;86460:599::-;-1:-1:-1;;;67505:9:0;;;67501:24;67494:32;;86893:12;86906:1;86893:15;;;;;;;;:::i;:::-;;;;;;:58;;;;;86997:12;87010:1;86997:15;;;;;;;;:::i;:::-;;;;;;;86988:6;:24;:55;;87042:1;86988:55;;;87024:12;87037:1;87024:15;;;;;;;;:::i;:::-;;;;;;;87015:6;:24;;;;:::i;:::-;86970:12;86983:1;86970:15;;;;;;;;:::i;:::-;;;;;;:73;;;;;86460:599;85569:1501;;;;85540:1740;;88363:234;88429:16;;-1:-1:-1;;;;;88429:16:0;88415:10;:30;88411:55;;88454:12;;-1:-1:-1;;;88454:12:0;;;;;;;;;;;88411:55;88479:10;:23;;88492:10;-1:-1:-1;;;;;;88479:23:0;;;;;;;-1:-1:-1;88513:29:0;;;;;;;;88560;;88492:10;;88560:29;;;88363:234::o;87393:200::-;87504:16;;;87518:1;87504:16;;;;;;;;;87451:27;;87504:16;;;;;;;;;;;-1:-1:-1;;87555:29:0;;-1:-1:-1;;;87555:29:0;;-1:-1:-1;;;;;819:32:1;;;87555:29:0;;;801:51:1;87491:29:0;;-1:-1:-1;87555:12:0;:22;;;;;;774:18:1;;87555:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;87531:10;87542:1;87531:13;;;;;;;;:::i;:::-;;;;;;:54;-1:-1:-1;;;;;87531:54:0;;;-1:-1:-1;;;;;87531:54:0;;;;;87393:200;;;:::o;87735:119::-;77882:10;;-1:-1:-1;;;;;77882:10:0;77868;:24;77864:49;;77901:12;;-1:-1:-1;;;77901:12:0;;;;;;;;;;;77864:49;87818:11:::1;:28:::0;87735:119::o;79951:1883::-;80075:28;;77980:10;-1:-1:-1;;;;;77994:8:0;77980:22;;77976:45;;78011:10;;-1:-1:-1;;;78011:10:0;;;;;;;;;;;77976:45;80270:29:::1;::::0;-1:-1:-1;;;80270:29:0;;-1:-1:-1;;;;;819:32:1;;;80270:29:0::1;::::0;::::1;801:51:1::0;80250:17:0::1;::::0;80270:12:::1;:22:::0;;::::1;::::0;::::1;::::0;774:18:1;;80270:29:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;80250:49:::0;-1:-1:-1;;;;;;80357:23:0;::::1;::::0;80353:1474:::1;;80446:16;::::0;;80460:1:::1;80446:16:::0;;;;;::::1;::::0;;;;::::1;::::0;::::1;::::0;;::::1;::::0;::::1;-1:-1:-1::0;;80492:16:0::1;::::0;;80506:1:::1;80492:16:::0;;;;;::::1;::::0;;80432:30;;-1:-1:-1;80492:16:0;80506:1;-1:-1:-1;80492:16:0::1;::::0;::::1;::::0;;::::1;::::0;::::1;;::::0;-1:-1:-1;80492:16:0::1;80477:31;;80542:9;80525:11;80537:1;80525:14;;;;;;;;:::i;:::-;;;;;;:26;-1:-1:-1::0;;;;;80525:26:0::1;;;-1:-1:-1::0;;;;;80525:26:0::1;;;::::0;::::1;76585:42;80566:11;80578:1;80566:14;;;;;;;;:::i;:::-;-1:-1:-1::0;;;;;80566:31:0;;::::1;:14;::::0;;::::1;::::0;;;;;:31;80787:42:::1;::::0;-1:-1:-1;;;80787:42:0;;76717::::1;80787;::::0;::::1;801:51:1::0;80787:22:0;;::::1;::::0;::::1;::::0;774:18:1;;80787:42:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;80703:59;::::0;-1:-1:-1;;;80703:59:0;;76717:42:::1;80703:59;::::0;::::1;801:51:1::0;-1:-1:-1;;;;;80703:39:0;::::1;::::0;::::1;::::0;774:18:1;;80703:59:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:126:::0;80681:815:::1;;80882:6;80864:12;80877:1;80864:15;;;;;;;;:::i;:::-;;;;;;:24;;;::::0;::::1;80353:1474;;80681:815;81025:47;::::0;-1:-1:-1;;;81025:47:0;;76585:42:::1;81025:47;::::0;::::1;801:51:1::0;81002:20:0::1;::::0;-1:-1:-1;;;;;81025:22:0;::::1;::::0;::::1;::::0;774:18:1;;81025:47:0::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;81002:70;;81175:11;81189:46;81207:5;81214:12;81228:6;81189:17;:46::i;:::-;81175:60;;81316:12;81310:3;:18;:74;;81383:1;81310:74;;;81331:49;81353:18;81359:12:::0;81353:3;:18:::1;:::i;:::-;-1:-1:-1::0;;;67505:9:0;;;67501:24;67494:32;;67341:203;81331:49:::1;81292:12;81305:1;81292:15;;;;;;;;:::i;:::-;;;;;;:92;;;::::0;::::1;81465:12;81478:1;81465:15;;;;;;;;:::i;:::-;;;;;;;81456:6;:24;;;;:::i;:::-;81438:12;81451:1;81438:15;;;;;;;;:::i;:::-;;;;;;:42;;;::::0;::::1;80910:586;;80353:1474;;88184:122:::0;77882:10;;-1:-1:-1;;;;;77882:10:0;77868;:24;77864:49;;77901:12;;-1:-1:-1;;;77901:12:0;;;;;;;;;;;77864:49;88268:16:::1;:30:::0;;-1:-1:-1;;;;;;88268:30:0::1;-1:-1:-1::0;;;;;88268:30:0;;;::::1;::::0;;;::::1;::::0;;88184:122::o;78812:661::-;79001:52;;-1:-1:-1;;;79001:52:0;;76585:42;79001:52;;;801:51:1;78885:17:0;;;;76861:42;;79001:36;;774:18:1;;79001:52:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;79088:56;;-1:-1:-1;;;79088:56:0;;76717:42;79088:56;;;801:51:1;78981:72:0;;-1:-1:-1;79064:21:0;;76861:42;;79088:36;;774:18:1;;79088:56:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;79245:42;;-1:-1:-1;;;79245:42:0;;76717;79245;;;801:51:1;79064:80:0;;-1:-1:-1;79221:21:0;;-1:-1:-1;;;;;79245:22:0;;;;;774:18:1;;79245:42:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;79221:66;-1:-1:-1;79361:18:0;;79357:109;;79408:46;:13;79429:9;79440:13;79408:20;:46::i;:::-;79396:58;;79357:109;78904:569;;;78812:661;;;:::o;83940:940::-;-1:-1:-1;;;;;84109:26:0;;84038:11;84109:26;;;:19;:26;;;;;;;;84062:73;;;;;;;;;;;;;;;;;;;;;84315:11;;84062:73;;84329:15;;84284:42;;;:::i;:::-;:60;:130;;;;-1:-1:-1;84393:21:0;84402:12;84393:6;:21;:::i;:::-;84365:24;;:49;;84284:130;84148:725;;;84490:24;;-1:-1:-1;84483:31:0;;84148:725;84594:34;84622:5;84594:27;:34::i;:::-;84588:40;;84686:11;;84701:1;84686:16;84682:180;;84806:40;;;;;;;;;;;84830:15;84806:40;;;;;;;-1:-1:-1;;;;;84777:26:0;;-1:-1:-1;84777:26:0;;;:19;:26;;;;;;;:69;;;;;;;;;;;84682:180;84051:829;83940:940;;;;;;:::o;50721:476::-;50793:9;51020:1;51016;51012:6;51008:14;51005:1;51002:21;50999:1;50995:29;50988:37;50985:1;50981:45;50971:172;;51060:10;51054:4;51047:24;51123:4;51117;51110:18;50971:172;-1:-1:-1;51166:9:0;;51162:17;;50721:476::o;14:131:1:-;-1:-1:-1;;;;;89:31:1;;79:42;;69:70;;135:1;132;125:12;69:70;14:131;:::o;150:247::-;209:6;262:2;250:9;241:7;237:23;233:32;230:52;;;278:1;275;268:12;230:52;317:9;304:23;336:31;361:5;336:31;:::i;863:315::-;931:6;939;992:2;980:9;971:7;967:23;963:32;960:52;;;1008:1;1005;998:12;960:52;1047:9;1034:23;1066:31;1091:5;1066:31;:::i;:::-;1116:5;1168:2;1153:18;;;;1140:32;;-1:-1:-1;;;863:315:1:o;1183:461::-;1236:3;1274:5;1268:12;1301:6;1296:3;1289:19;1327:4;1356:2;1351:3;1347:12;1340:19;;1393:2;1386:5;1382:14;1414:1;1424:195;1438:6;1435:1;1432:13;1424:195;;;1503:13;;-1:-1:-1;;;;;1499:39:1;1487:52;;1559:12;;;;1594:15;;;;1535:1;1453:9;1424:195;;;-1:-1:-1;1635:3:1;;1183:461;-1:-1:-1;;;;;1183:461:1:o;1649:804::-;1906:2;1895:9;1888:21;1869:4;1932:56;1984:2;1973:9;1969:18;1961:6;1932:56;:::i;:::-;2045:22;;;2007:2;2025:18;;;2018:50;;;;2117:13;;2139:22;;;2215:15;;;;2177;;;2248:1;2258:169;2272:6;2269:1;2266:13;2258:169;;;2333:13;;2321:26;;2402:15;;;;2367:12;;;;2294:1;2287:9;2258:169;;;-1:-1:-1;2444:3:1;;1649:804;-1:-1:-1;;;;;;;1649:804:1:o;2458:261::-;2637:2;2626:9;2619:21;2600:4;2657:56;2709:2;2698:9;2694:18;2686:6;2657:56;:::i;2724:180::-;2783:6;2836:2;2824:9;2815:7;2811:23;2807:32;2804:52;;;2852:1;2849;2842:12;2804:52;-1:-1:-1;2875:23:1;;2724:180;-1:-1:-1;2724:180:1:o;3322:251::-;3392:6;3445:2;3433:9;3424:7;3420:23;3416:32;3413:52;;;3461:1;3458;3451:12;3413:52;3493:9;3487:16;3512:31;3537:5;3512:31;:::i;3710:127::-;3771:10;3766:3;3762:20;3759:1;3752:31;3802:4;3799:1;3792:15;3826:4;3823:1;3816:15;3842:127;3903:10;3898:3;3894:20;3891:1;3884:31;3934:4;3931:1;3924:15;3958:4;3955:1;3948:15;3974:128;4041:9;;;4062:11;;;4059:37;;;4076:18;;:::i;:::-;3974:128;;;;:::o;4107:184::-;4177:6;4230:2;4218:9;4209:7;4205:23;4201:32;4198:52;;;4246:1;4243;4236:12;4198:52;-1:-1:-1;4269:16:1;;4107:184;-1:-1:-1;4107:184:1:o;4296:125::-;4361:9;;;4382:10;;;4379:36;;;4395:18;;:::i
Swarm Source
ipfs://8472621cc88d0b1c814e6c42ec9459e8966e9586475b3b12de770e9dd7f3f941
Loading...
Loading
Loading...
Loading

Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.