More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
19408597 | 289 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
VestingSimple
Compiler Version
v0.8.21+commit.d9974bed
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.21; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import { SafeTransferLib } from "@solmate/utils/SafeTransferLib.sol"; import { ERC20 } from "@solmate/tokens/ERC20.sol"; import { Math } from "src/utils/Math.sol"; /** * @title Cellar Vesting Timelock * @author Kevin Kennis * @notice A contract set as a position in a Sommelier cellar, with an adapter, * that linearly releases deposited tokens in order to smooth * out sudden TVL increases. */ contract VestingSimple { using SafeTransferLib for ERC20; using EnumerableSet for EnumerableSet.UintSet; using Math for uint256; /// @notice Emitted when tokens are deposited for vesting. /// /// @param user The user making the deposit. /// @param receiver The user receiving the shares. /// @param amount The amount of tokens deposited. event VestingDeposit(address indexed user, address indexed receiver, uint256 amount); /// @notice Emitted when vested tokens are withdrawn. /// /// @param user The owner of the deposit. /// @param receiver The user receiving the deposit. /// @param depositId The ID of the deposit specified. /// @param amount The amount of tokens deposited. event VestingWithdraw(address indexed user, address indexed receiver, uint256 depositId, uint256 amount); // ============================================= ERRORS ============================================= /// @notice Contract was deployed with no asset. error Vesting_ZeroAsset(); /// @notice Contract was deployed with no vesting period. error Vesting_ZeroVestingPeriod(); /// @notice Contract was deployed with a minimum deposit lower /// then the vesting period. /// /// @param lowestMinimum The lowest minimum deposit possible, /// based on the vesting period. error Vesting_MinimumTooSmall(uint256 lowestMinimum); /// @notice User attempted to deposit 0 tokens. error Vesting_ZeroDeposit(); /// @notice User attempted to deposit an amount of tokens /// under the minimum. /// /// @param minimumDeposit The minimum deposit amount. error Vesting_DepositTooSmall(uint256 minimumDeposit); /// @notice User attempted to withdraw 0 tokens. error Vesting_ZeroWithdraw(); /// @notice User attempted to withdraw from a fully-vested deposit. /// /// @param depositId The deposit ID specified. error Vesting_DepositFullyVested(uint256 depositId); /// @notice User attempted to withdraw more than the amount vested, /// from any deposit. /// /// @param available The amount of token available for withdrawal. error Vesting_NotEnoughAvailable(uint256 available); /// @notice User attempted to withdraw more than the amount vested. /// /// @param depositId The deposit ID specified. /// @param available The amount of token available for withdrawal. error Vesting_DepositNotEnoughAvailable(uint256 depositId, uint256 available); /// @notice User specified a deposit that does not exist. /// /// @param depositId The deposit ID specified. error Vesting_NoDeposit(uint256 depositId); // ============================================= TYPES ============================================= /// @notice Contains all information needed to vest /// tokens for each deposited. struct VestingSchedule { uint256 amountPerSecond; // The amount of tokens vested per second. uint128 until; // The time vesting will finish. uint128 lastClaimed; // The last time vesting accrued. uint256 vested; // The amount of vested tokens still not withdrawn. } // ============================================= STATE ============================================= /// @notice Used for retaining maximum precision in amountPerSecond. uint256 internal constant ONE = 1e18; /// @notice The deposit token for the vesting contract. ERC20 public immutable asset; /// @notice The vesting period for the contract, in seconds. uint256 public immutable vestingPeriod; /// @notice Used to preclude rounding errors. Should be equal to 0.0001 tokens of asset. uint256 public immutable minimumDeposit; /// @notice All vesting schedules for a user mapping(address => mapping(uint256 => VestingSchedule)) public vests; /// @notice Enumeration of user deposit ID mapping(address => EnumerableSet.UintSet) private allUserDepositIds; /// @notice The current user's last deposited vest mapping(address => uint256) public currentId; /// @notice The total amount of deposits to the contract. uint256 public totalDeposits; /// @notice The total amount of deposits to the contract that haven't vested /// through withdrawals. Note that based on point-of-time calculations, /// some of these tokens may be available for withdrawal. uint256 public unvestedDeposits; // ========================================== CONSTRUCTOR ========================================== /** * @notice Instantiate the contract with a vesting period. * * @param _asset The token the vesting contract will hold. * @param _vestingPeriod The length of time, in seconds, that tokens should vest over. * @param _minimumDeposit The minimum amount of tokens that can be deposited for vesting. */ constructor(ERC20 _asset, uint256 _vestingPeriod, uint256 _minimumDeposit) { if (address(_asset) == address(0)) revert Vesting_ZeroDeposit(); if (_vestingPeriod == 0) revert Vesting_ZeroVestingPeriod(); if (_minimumDeposit < _vestingPeriod) revert Vesting_MinimumTooSmall(_vestingPeriod); asset = _asset; vestingPeriod = _vestingPeriod; minimumDeposit = _minimumDeposit; } // ====================================== DEPOSIT/WITHDRAWAL ======================================= /** * @notice Deposit tokens to vest, which will instantly * start emitting linearly over the defined lock period. Each deposit * tracked separately such that new deposits don't reset the vesting * clocks of the old deposits. * * @param assets The amount of tokens to deposit. * @param receiver The account credited for the deposit. * * @return shares The amount of tokens deposited (for compatibility). */ function deposit(uint256 assets, address receiver) public returns (uint256 shares) { // Check for rounding error since we round down in previewDeposit. if (assets == 0) revert Vesting_ZeroDeposit(); if (assets < minimumDeposit) revert Vesting_DepositTooSmall(minimumDeposit); // Used for compatibility shares = assets; // Add deposit info uint256 newDepositId = ++currentId[receiver]; allUserDepositIds[receiver].add(newDepositId); VestingSchedule storage s = vests[receiver][newDepositId]; s.amountPerSecond = assets.mulDivDown(ONE, vestingPeriod); s.until = uint128(block.timestamp + vestingPeriod); s.lastClaimed = uint128(block.timestamp); // Update global accounting totalDeposits += assets; unvestedDeposits += assets; // Collect tokens ERC20(asset).safeTransferFrom(msg.sender, address(this), assets); emit VestingDeposit(msg.sender, receiver, assets); } /** * @notice Withdraw vesting tokens, winding the vesting clock * and releasing newly earned tokens since the last claim. * Reverts if there are not enough assets available. * * @param depositId The deposit ID to withdraw from. * @param assets The amount of assets to withdraw. * * @return shares The amount of tokens withdrawn (for compatibility). */ function withdraw(uint256 depositId, uint256 assets) public returns (uint256 shares) { // Check for rounding error since we round down in previewDeposit. if (assets == 0) revert Vesting_ZeroWithdraw(); // Used for compatibility shares = assets; VestingSchedule storage s = vests[msg.sender][depositId]; uint256 newlyVested = _vestDeposit(msg.sender, depositId); if (newlyVested == 0 && s.vested == 0) revert Vesting_DepositFullyVested(depositId); if (assets > s.vested) revert Vesting_DepositNotEnoughAvailable(depositId, s.vested); // Update accounting s.vested -= assets; totalDeposits -= assets; // Remove deposit if needed, including 1-wei deposits (rounding) if (s.vested <= 1 && block.timestamp >= s.until) { allUserDepositIds[msg.sender].remove(depositId); } emit VestingWithdraw(msg.sender, msg.sender, depositId, assets); asset.safeTransfer(msg.sender, assets); } /** * @notice Withdraw all tokens across all deposits that have vested. * Winds the vesting clock to release newly earned tokens since the last claim. * * @return shares The amount of tokens withdrawn (for compatibility). */ function withdrawAll() public returns (uint256 shares) { uint256[] memory depositIds = allUserDepositIds[msg.sender].values(); uint256 numDeposits = depositIds.length; for (uint256 i = 0; i < numDeposits; i++) { VestingSchedule storage s = vests[msg.sender][depositIds[i]]; if (s.amountPerSecond > 0 && (s.vested > 0 || s.lastClaimed < s.until)) { _vestDeposit(msg.sender, depositIds[i]); uint256 vested = s.vested; shares += vested; s.vested = 0; // Remove deposit if needed // Will not affect loop logic because values are pre-defined if (s.vested == 0 && block.timestamp >= s.until) { allUserDepositIds[msg.sender].remove(depositIds[i]); } emit VestingWithdraw(msg.sender, msg.sender, depositIds[i], vested); } } totalDeposits -= shares; asset.safeTransfer(msg.sender, shares); } /** * @notice Withdraw a specified amount of tokens, sending them to the specified * receiver. Withdraws from all deposits in order until the current amount * is met. * * @param assets The amount of assets to withdraw. * @param receiver The address that will receive the assets. * * @return shares The amount of tokens withdrawn (for compatibility). */ function withdrawAnyFor(uint256 assets, address receiver) public returns (uint256 shares) { uint256[] memory depositIds = allUserDepositIds[msg.sender].values(); uint256 numDeposits = depositIds.length; shares = assets; for (uint256 i = 0; assets > 0 && i < numDeposits; i++) { VestingSchedule storage s = vests[msg.sender][depositIds[i]]; if (s.amountPerSecond > 0 && (s.vested > 0 || s.lastClaimed < s.until)) { _vestDeposit(msg.sender, depositIds[i]); uint256 payout = s.vested >= assets ? assets : s.vested; if (payout == assets) { // Can end here - only withdraw the amount we need s.vested -= payout; assets = 0; } else { // Withdraw full deposit and go to next one assets -= payout; s.vested = 0; } emit VestingWithdraw(msg.sender, receiver, depositIds[i], payout); // Remove deposit if needed // Will not affect loop logic because values are pre-defined if (s.vested == 0 && block.timestamp >= s.until) { allUserDepositIds[msg.sender].remove(depositIds[i]); } } } // Could not collect enough if (assets > 0) revert Vesting_NotEnoughAvailable(shares - assets); totalDeposits -= shares; asset.safeTransfer(receiver, shares); } // ======================================= VIEW FUNCTIONS ========================================= /** * @notice Reports all tokens which are vested and can be withdrawn for a user. * * @param user The user whose balance should be reported. * * @return balance The user's vested total balance. */ function vestedBalanceOf(address user) public view returns (uint256 balance) { uint256[] memory depositIds = allUserDepositIds[user].values(); uint256 numDeposits = depositIds.length; for (uint256 i = 0; i < numDeposits; i++) { VestingSchedule storage s = vests[user][depositIds[i]]; if (s.amountPerSecond > 0 && (s.vested > 0 || s.lastClaimed < s.until)) { uint256 lastTimestamp = block.timestamp <= s.until ? block.timestamp : s.until; uint256 timeElapsed = lastTimestamp - s.lastClaimed; uint256 newlyVested = timeElapsed.mulDivDown(s.amountPerSecond, ONE); balance += (s.vested + newlyVested); } } } /** * @notice Reports all tokens which are vested and can be withdrawn for a user. * * @param user The user whose balance should be reported. * @param depositId The depositId to report. * * @return balance The user's vested balance for the specified deposit. */ function vestedBalanceOfDeposit(address user, uint256 depositId) public view returns (uint256) { VestingSchedule storage s = vests[user][depositId]; if (s.amountPerSecond == 0) revert Vesting_NoDeposit(depositId); uint256 lastTimestamp = block.timestamp <= s.until ? block.timestamp : s.until; uint256 timeElapsed = lastTimestamp - s.lastClaimed; uint256 newlyVested = timeElapsed.mulDivDown(s.amountPerSecond, ONE); return s.vested + newlyVested; } /** * @notice Reports all tokens deposited by a user which have not been withdrawn yet. * Includes unvested tokens. * * @param user The user whose balance should be reported. * * @return balance The user's total balance, both vested and unvested. */ function totalBalanceOf(address user) public view returns (uint256 balance) { uint256[] memory depositIds = allUserDepositIds[user].values(); uint256 numDeposits = depositIds.length; for (uint256 i = 0; i < numDeposits; i++) { VestingSchedule storage s = vests[user][depositIds[i]]; if (s.amountPerSecond > 0 && (s.vested > 0 || s.lastClaimed < s.until)) { uint256 startTime = s.until - vestingPeriod; uint256 timeElapsedBeforeClaim = s.lastClaimed - startTime; uint256 totalAmount = (s.amountPerSecond * vestingPeriod) / ONE; uint256 previouslyVested = timeElapsedBeforeClaim.mulDivDown(s.amountPerSecond, ONE); uint256 claimed = previouslyVested - s.vested; balance += (totalAmount - claimed); } } } /** * @notice Returns all deposit IDs in an array. Only contains active deposits. * * @param user The user whose IDs should be reported. * * @return ids An array of the user's active deposit IDs. */ function userDepositIds(address user) public view returns (uint256[] memory) { return allUserDepositIds[user].values(); } /** * @notice Returns the vesting info for a given sdeposit. * * @param user The user whose vesting info should be reported. * @param depositId The deposit to report. * * @return amountPerSecond The amount of tokens released per second. * @return until The timestamp at which all coins will be released. * @return lastClaimed The last time vesting occurred. * @return amountPerSecond The amount of tokens released per second. */ function userVestingInfo(address user, uint256 depositId) public view returns (uint256, uint128, uint128, uint256) { VestingSchedule memory s = vests[user][depositId]; return (s.amountPerSecond, s.until, s.lastClaimed, s.vested); } // ===================================== INTERNAL FUNCTIONS ======================================= /** * @dev Wind the vesting clock for a given deposit, based on how many seconds have * elapsed in the vesting schedule since the last claim. * * @param user The user whose deposit will be vested. * @param depositId The deposit ID to vest for. * * @return newlyVested The newly vested tokens since the last vest. */ function _vestDeposit(address user, uint256 depositId) internal returns (uint256 newlyVested) { // Add deposit info VestingSchedule storage s = vests[user][depositId]; if (s.amountPerSecond == 0) revert Vesting_NoDeposit(depositId); // No new vesting if (s.lastClaimed >= s.until) return 0; uint256 lastTimestamp = block.timestamp <= s.until ? block.timestamp : s.until; uint256 timeElapsed = lastTimestamp - s.lastClaimed; // In case there were rounding errors due to accrual times, // round up on the last vest to collect anything lost. if (lastTimestamp == s.until) { newlyVested = timeElapsed.mulDivUp(s.amountPerSecond, ONE); } else { newlyVested = timeElapsed.mulDivDown(s.amountPerSecond, ONE); } s.vested += newlyVested; s.lastClaimed = uint128(lastTimestamp); unvestedDeposits -= newlyVested; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "../tokens/ERC20.sol"; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. library SafeTransferLib { /*////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool success; /// @solidity memory-safe-assembly assembly { // Transfer the ETH and store if it succeeded or not. success := call(gas(), to, amount, 0, 0, 0, 0) } require(success, "ETH_TRANSFER_FAILED"); } /*////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument. mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) ) } require(success, "TRANSFER_FROM_FAILED"); } function safeTransfer( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "TRANSFER_FAILED"); } function safeApprove( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "APPROVE_FAILED"); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.21; library Math { /** * @notice Substract with a floor of 0 for the result. */ function subMinZero(uint256 x, uint256 y) internal pure returns (uint256) { return x > y ? x - y : 0; } /** * @notice Used to change the decimals of precision used for an amount. */ function changeDecimals(uint256 amount, uint8 fromDecimals, uint8 toDecimals) internal pure returns (uint256) { if (fromDecimals == toDecimals) { return amount; } else if (fromDecimals < toDecimals) { return amount * 10 ** (toDecimals - fromDecimals); } else { return amount / 10 ** (fromDecimals - toDecimals); } } // ===================================== OPENZEPPELIN'S MATH ===================================== function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } // ================================= SOLMATE's FIXEDPOINTMATHLIB ================================= uint256 public constant WAD = 1e18; // The scalar of ETH and most ERC20s. function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. } function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) { assembly { // Store x * y in z for now. z := mul(x, y) // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { revert(0, 0) } // Divide z by the denominator. z := div(z, denominator) } } function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) { assembly { // Store x * y in z for now. z := mul(x, y) // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { revert(0, 0) } // First, divide z - 1 by the denominator and add 1. // We allow z - 1 to underflow if z is 0, because we multiply the // end result by 0 if z is zero, ensuring we return 0 if z is zero. z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1)) } } }
{ "remappings": [ "@solmate/=lib/solmate/src/", "@forge-std/=lib/forge-std/src/", "@ds-test/=lib/forge-std/lib/ds-test/src/", "ds-test/=lib/forge-std/lib/ds-test/src/", "@openzeppelin/=lib/openzeppelin-contracts/", "@uniswap/v3-periphery/=lib/v3-periphery/", "@uniswap/v3-core/=lib/v3-core/", "@chainlink/=lib/chainlink/", "@uniswapV3P/=lib/v3-periphery/contracts/", "@uniswapV3C/=lib/v3-core/contracts/", "@balancer/=lib/balancer-v2-monorepo/pkg/", "@ccip/=lib/ccip/", "@balancer-labs/=lib/balancer-v2-monorepo/../../node_modules/@balancer-labs/", "axelar-gmp-sdk-solidity/=lib/axelar-gmp-sdk-solidity/contracts/", "balancer-v2-monorepo/=lib/balancer-v2-monorepo/", "ccip/=lib/ccip/contracts/", "chainlink/=lib/chainlink/integration-tests/contracts/ethereum/src/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "pendle-core-v2-public/=lib/pendle-core-v2-public/contracts/", "solmate/=lib/solmate/src/", "v3-core/=lib/v3-core/contracts/", "v3-periphery/=lib/v3-periphery/contracts/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "shanghai", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract ERC20","name":"_asset","type":"address"},{"internalType":"uint256","name":"_vestingPeriod","type":"uint256"},{"internalType":"uint256","name":"_minimumDeposit","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"Vesting_DepositFullyVested","type":"error"},{"inputs":[{"internalType":"uint256","name":"depositId","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"name":"Vesting_DepositNotEnoughAvailable","type":"error"},{"inputs":[{"internalType":"uint256","name":"minimumDeposit","type":"uint256"}],"name":"Vesting_DepositTooSmall","type":"error"},{"inputs":[{"internalType":"uint256","name":"lowestMinimum","type":"uint256"}],"name":"Vesting_MinimumTooSmall","type":"error"},{"inputs":[{"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"Vesting_NoDeposit","type":"error"},{"inputs":[{"internalType":"uint256","name":"available","type":"uint256"}],"name":"Vesting_NotEnoughAvailable","type":"error"},{"inputs":[],"name":"Vesting_ZeroAsset","type":"error"},{"inputs":[],"name":"Vesting_ZeroDeposit","type":"error"},{"inputs":[],"name":"Vesting_ZeroVestingPeriod","type":"error"},{"inputs":[],"name":"Vesting_ZeroWithdraw","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"VestingDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"VestingWithdraw","type":"event"},{"inputs":[],"name":"asset","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"currentId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minimumDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"totalBalanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDeposits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unvestedDeposits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"userDepositIds","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"userVestingInfo","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint128","name":"","type":"uint128"},{"internalType":"uint128","name":"","type":"uint128"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"vestedBalanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"vestedBalanceOfDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"vests","outputs":[{"internalType":"uint256","name":"amountPerSecond","type":"uint256"},{"internalType":"uint128","name":"until","type":"uint128"},{"internalType":"uint128","name":"lastClaimed","type":"uint128"},{"internalType":"uint256","name":"vested","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"depositId","type":"uint256"},{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAll","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"withdrawAnyFor","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60e060405234801561000f575f80fd5b506040516116e93803806116e983398101604081905261002e916100b6565b6001600160a01b038316610055576040516331ac19c360e11b815260040160405180910390fd5b815f0361007557604051634fd2026b60e01b815260040160405180910390fd5b8181101561009d5760405163b14d7e9f60e01b81526004810183905260240160405180910390fd5b6001600160a01b0390921660805260a05260c0526100f5565b5f805f606084860312156100c8575f80fd5b83516001600160a01b03811681146100de575f80fd5b602085015160409095015190969495509392505050565b60805160a05160c0516115846101655f395f81816101c1015281816108f8015261092d01525f81816102a4015281816107de0152818161083f015281816109db0152610a0601525f8181610149015281816106f301528181610a8401528181610d4a0152610f1001526115845ff3fe608060405234801561000f575f80fd5b50600436106100fb575f3560e01c80636e553f651161009357806384a86e6d1161006357806384a86e6d146102e2578063853828b61461032d578063a14c6e1614610335578063cd54256314610355575f80fd5b80636e553f651461028c5780637313ee5a1461029f57806378c51689146102c65780637d882097146102d9575f80fd5b8063441a3e70116100ce578063441a3e70146101965780634b0ee02a146101a9578063636bfbab146101bc5780636ce73df7146101e3575f80fd5b80630e2d1a2a146100ff5780632299b8fa1461012557806338d52e0f146101445780634089d6d214610183575b5f80fd5b61011261010d3660046113d0565b61035e565b6040519081526020015b60405180910390f35b6101126101333660046113d0565b60026020525f908152604090205481565b61016b7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161011c565b6101126101913660046113e9565b6104b5565b6101126101a4366004611411565b610588565b6101126101b73660046113d0565b610722565b6101127f000000000000000000000000000000000000000000000000000000000000000081565b61025c6101f13660046113e9565b6001600160a01b03919091165f90815260208181526040808320938352928152908290208251608081018452815480825260018301546001600160801b03808216958401869052600160801b9091041694820185905260029092015460609091018190529093919291565b60405161011c94939291909384526001600160801b03928316602085015291166040830152606082015260800190565b61011261029a366004611431565b6108d5565b6101127f000000000000000000000000000000000000000000000000000000000000000081565b6101126102d4366004611431565b610af4565b61011260035481565b61025c6102f03660046113e9565b5f60208181529281526040808220909352908152208054600182015460029092015490916001600160801b0380821692600160801b909204169084565b610112610d71565b6103486103433660046113d0565b610f3c565b60405161011c919061145b565b61011260045481565b6001600160a01b0381165f908152600160205260408120819061038090610f5b565b80519091505f5b818110156104ad576001600160a01b0385165f908152602081905260408120845182908690859081106103bc576103bc61149e565b602002602001015181526020019081526020015f2090505f815f015411801561040857505f81600201541180610408575060018101546001600160801b03808216600160801b90920416105b1561049a5760018101545f906001600160801b03164211156104375760018201546001600160801b0316610439565b425b60018301549091505f9061045d90600160801b90046001600160801b0316836114c6565b83549091505f90610478908390670de0b6b3a7640000610f6e565b905080846002015461048a91906114d9565b61049490896114d9565b97505050505b50806104a5816114ec565b915050610387565b505050919050565b6001600160a01b0382165f908152602081815260408083208484529091528120805482036104fe5760405163a1632d2b60e01b8152600481018490526024015b60405180910390fd5b60018101545f906001600160801b03164211156105285760018201546001600160801b031661052a565b425b60018301549091505f9061054e90600160801b90046001600160801b0316836114c6565b83549091505f90610569908390670de0b6b3a7640000610f6e565b905080846002015461057b91906114d9565b9450505050505b92915050565b5f815f036105a957604051630672e93f60e01b815260040160405180910390fd5b50335f81815260208181526040808320868452909152812083929091906105d09086610f8c565b9050801580156105e257506002820154155b1561060357604051632f8d18dd60e21b8152600481018690526024016104f5565b816002015484111561063857600282015460405163724e551560e01b81526104f5918791600401918252602082015260400190565b83826002015f82825461064b91906114c6565b925050819055508360035f82825461066391906114c6565b9091555050600282015460011080159061068a575060018201546001600160801b03164210155b156106a957335f9081526001602052604090206106a790866110ea565b505b6040805186815260208101869052339182917f8f7d9ca2df6db2956b58e8476c3d797b62eacd55fe9df5d1cb12aab498333243910160405180910390a361071a6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633866110f5565b505092915050565b6001600160a01b0381165f908152600160205260408120819061074490610f5b565b80519091505f5b818110156104ad576001600160a01b0385165f908152602081905260408120845182908690859081106107805761078061149e565b602002602001015181526020019081526020015f2090505f815f01541180156107cc57505f816002015411806107cc575060018101546001600160801b03808216600160801b90920416105b156108c25760018101545f9061080c907f0000000000000000000000000000000000000000000000000000000000000000906001600160801b03166114c6565b60018301549091505f90610831908390600160801b90046001600160801b03166114c6565b90505f670de0b6b3a76400007f0000000000000000000000000000000000000000000000000000000000000000855f015461086c9190611504565b610876919061151b565b84549091505f90610891908490670de0b6b3a7640000610f6e565b90505f8560020154826108a491906114c6565b90506108b081846114c6565b6108ba908b6114d9565b995050505050505b50806108cd816114ec565b91505061074b565b5f825f036108f6576040516331ac19c360e11b815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000083101561095957604051633e32c46f60e01b81527f000000000000000000000000000000000000000000000000000000000000000060048201526024016104f5565b506001600160a01b0381165f9081526002602052604081208054849291908290610982906114ec565b91829055506001600160a01b0384165f9081526001602052604090209091506109ab908261116f565b506001600160a01b0383165f9081526020818152604080832084845290915290206109ff85670de0b6b3a76400007f0000000000000000000000000000000000000000000000000000000000000000610f6e565b8155610a2b7f0000000000000000000000000000000000000000000000000000000000000000426114d9565b426001600160801b03908116600160801b029116176001820155600380548691905f90610a599084906114d9565b925050819055508460045f828254610a7191906114d9565b90915550610aac90506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633308861117a565b6040518581526001600160a01b0385169033907f4a73c8b9b5a1c76f8a33e65d5155ab65d9a96b0ae8d7ec834a5678536cebeda79060200160405180910390a3505092915050565b335f9081526001602052604081208190610b0d90610f5b565b80518593509091505f5b5f86118015610b2557508181105b15610cf957335f90815260208190526040812084518290869085908110610b4e57610b4e61149e565b602002602001015181526020019081526020015f2090505f815f0154118015610b9a57505f81600201541180610b9a575060018101546001600160801b03808216600160801b90920416105b15610ce657610bc233858481518110610bb557610bb561149e565b6020026020010151610f8c565b505f8782600201541015610bda578160020154610bdc565b875b9050878103610c065780826002015f828254610bf891906114c6565b909155505f9850610c199050565b610c1081896114c6565b5f600284015597505b866001600160a01b0316336001600160a01b03167f8f7d9ca2df6db2956b58e8476c3d797b62eacd55fe9df5d1cb12aab498333243878681518110610c6057610c6061149e565b602002602001015184604051610c80929190918252602082015260400190565b60405180910390a36002820154158015610ca7575060018201546001600160801b03164210155b15610ce457610ce2858481518110610cc157610cc161149e565b602090810291909101810151335f90815260019092526040909120906110ea565b505b505b5080610cf1816114ec565b915050610b17565b508415610d2657610d0a85846114c6565b6040516303c64f5960e51b81526004016104f591815260200190565b8260035f828254610d3791906114c6565b9091555061071a90506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001685856110f5565b335f9081526001602052604081208190610d8a90610f5b565b80519091505f5b81811015610eeb57335f90815260208190526040812084518290869085908110610dbd57610dbd61149e565b602002602001015181526020019081526020015f2090505f815f0154118015610e0957505f81600201541180610e09575060018101546001600160801b03808216600160801b90920416105b15610ed857610e2433858481518110610bb557610bb561149e565b506002810154610e3481876114d9565b5f6002840155955060018201546001600160801b03164210610e6757610e65858481518110610cc157610cc161149e565b505b336001600160a01b0316336001600160a01b03167f8f7d9ca2df6db2956b58e8476c3d797b62eacd55fe9df5d1cb12aab498333243878681518110610eae57610eae61149e565b602002602001015184604051610ece929190918252602082015260400190565b60405180910390a3505b5080610ee3816114ec565b915050610d91565b508260035f828254610efd91906114c6565b90915550610f3790506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633856110f5565b505090565b6001600160a01b0381165f908152600160205260409020606090610582905b60605f610f6783611200565b9392505050565b828202811515841585830485141716610f85575f80fd5b0492915050565b6001600160a01b0382165f90815260208181526040808320848452909152812080548203610fd05760405163a1632d2b60e01b8152600481018490526024016104f5565b60018101546001600160801b03808216600160801b9092041610610ff7575f915050610582565b60018101545f906001600160801b03164211156110215760018201546001600160801b0316611023565b425b60018301549091505f9061104790600160801b90046001600160801b0316836114c6565b60018401549091506001600160801b0316820361107b578254611074908290670de0b6b3a7640000611259565b9350611094565b8254611091908290670de0b6b3a7640000610f6e565b93505b83836002015f8282546110a791906114d9565b90915550506001830180546001600160801b03808516600160801b029116179055600480548591905f906110dc9084906114c6565b909155505050505092915050565b5f610f678383611286565b5f60405163a9059cbb60e01b815283600482015282602482015260205f6044835f895af13d15601f3d1160015f5114161716915050806111695760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b60448201526064016104f5565b50505050565b5f610f678383611369565b5f6040516323b872dd60e01b815284600482015283602482015282604482015260205f6064835f8a5af13d15601f3d1160015f5114161716915050806111f95760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b60448201526064016104f5565b5050505050565b6060815f0180548060200260200160405190810160405280929190818152602001828054801561124d57602002820191905f5260205f20905b815481526020019060010190808311611239575b50505050509050919050565b828202811515841585830485141716611270575f80fd5b6001826001830304018115150290509392505050565b5f8181526001830160205260408120548015611360575f6112a86001836114c6565b85549091505f906112bb906001906114c6565b905081811461131a575f865f0182815481106112d9576112d961149e565b905f5260205f200154905080875f0184815481106112f9576112f961149e565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061132b5761132b61153a565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050610582565b5f915050610582565b5f8181526001830160205260408120546113ae57508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610582565b505f610582565b80356001600160a01b03811681146113cb575f80fd5b919050565b5f602082840312156113e0575f80fd5b610f67826113b5565b5f80604083850312156113fa575f80fd5b611403836113b5565b946020939093013593505050565b5f8060408385031215611422575f80fd5b50508035926020909101359150565b5f8060408385031215611442575f80fd5b82359150611452602084016113b5565b90509250929050565b602080825282518282018190525f9190848201906040850190845b8181101561149257835183529284019291840191600101611476565b50909695505050505050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b81810381811115610582576105826114b2565b80820180821115610582576105826114b2565b5f600182016114fd576114fd6114b2565b5060010190565b8082028115828204841417610582576105826114b2565b5f8261153557634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52603160045260245ffdfea264697066735822122058211de2cd20b07a68fe0fac8002492e910688cc452e787709b89f56b7c9fc0564736f6c63430008150033000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000002386f26fc10000
Deployed Bytecode
0x608060405234801561000f575f80fd5b50600436106100fb575f3560e01c80636e553f651161009357806384a86e6d1161006357806384a86e6d146102e2578063853828b61461032d578063a14c6e1614610335578063cd54256314610355575f80fd5b80636e553f651461028c5780637313ee5a1461029f57806378c51689146102c65780637d882097146102d9575f80fd5b8063441a3e70116100ce578063441a3e70146101965780634b0ee02a146101a9578063636bfbab146101bc5780636ce73df7146101e3575f80fd5b80630e2d1a2a146100ff5780632299b8fa1461012557806338d52e0f146101445780634089d6d214610183575b5f80fd5b61011261010d3660046113d0565b61035e565b6040519081526020015b60405180910390f35b6101126101333660046113d0565b60026020525f908152604090205481565b61016b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6040516001600160a01b03909116815260200161011c565b6101126101913660046113e9565b6104b5565b6101126101a4366004611411565b610588565b6101126101b73660046113d0565b610722565b6101127f000000000000000000000000000000000000000000000000002386f26fc1000081565b61025c6101f13660046113e9565b6001600160a01b03919091165f90815260208181526040808320938352928152908290208251608081018452815480825260018301546001600160801b03808216958401869052600160801b9091041694820185905260029092015460609091018190529093919291565b60405161011c94939291909384526001600160801b03928316602085015291166040830152606082015260800190565b61011261029a366004611431565b6108d5565b6101127f0000000000000000000000000000000000000000000000000000000000093a8081565b6101126102d4366004611431565b610af4565b61011260035481565b61025c6102f03660046113e9565b5f60208181529281526040808220909352908152208054600182015460029092015490916001600160801b0380821692600160801b909204169084565b610112610d71565b6103486103433660046113d0565b610f3c565b60405161011c919061145b565b61011260045481565b6001600160a01b0381165f908152600160205260408120819061038090610f5b565b80519091505f5b818110156104ad576001600160a01b0385165f908152602081905260408120845182908690859081106103bc576103bc61149e565b602002602001015181526020019081526020015f2090505f815f015411801561040857505f81600201541180610408575060018101546001600160801b03808216600160801b90920416105b1561049a5760018101545f906001600160801b03164211156104375760018201546001600160801b0316610439565b425b60018301549091505f9061045d90600160801b90046001600160801b0316836114c6565b83549091505f90610478908390670de0b6b3a7640000610f6e565b905080846002015461048a91906114d9565b61049490896114d9565b97505050505b50806104a5816114ec565b915050610387565b505050919050565b6001600160a01b0382165f908152602081815260408083208484529091528120805482036104fe5760405163a1632d2b60e01b8152600481018490526024015b60405180910390fd5b60018101545f906001600160801b03164211156105285760018201546001600160801b031661052a565b425b60018301549091505f9061054e90600160801b90046001600160801b0316836114c6565b83549091505f90610569908390670de0b6b3a7640000610f6e565b905080846002015461057b91906114d9565b9450505050505b92915050565b5f815f036105a957604051630672e93f60e01b815260040160405180910390fd5b50335f81815260208181526040808320868452909152812083929091906105d09086610f8c565b9050801580156105e257506002820154155b1561060357604051632f8d18dd60e21b8152600481018690526024016104f5565b816002015484111561063857600282015460405163724e551560e01b81526104f5918791600401918252602082015260400190565b83826002015f82825461064b91906114c6565b925050819055508360035f82825461066391906114c6565b9091555050600282015460011080159061068a575060018201546001600160801b03164210155b156106a957335f9081526001602052604090206106a790866110ea565b505b6040805186815260208101869052339182917f8f7d9ca2df6db2956b58e8476c3d797b62eacd55fe9df5d1cb12aab498333243910160405180910390a361071a6001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21633866110f5565b505092915050565b6001600160a01b0381165f908152600160205260408120819061074490610f5b565b80519091505f5b818110156104ad576001600160a01b0385165f908152602081905260408120845182908690859081106107805761078061149e565b602002602001015181526020019081526020015f2090505f815f01541180156107cc57505f816002015411806107cc575060018101546001600160801b03808216600160801b90920416105b156108c25760018101545f9061080c907f0000000000000000000000000000000000000000000000000000000000093a80906001600160801b03166114c6565b60018301549091505f90610831908390600160801b90046001600160801b03166114c6565b90505f670de0b6b3a76400007f0000000000000000000000000000000000000000000000000000000000093a80855f015461086c9190611504565b610876919061151b565b84549091505f90610891908490670de0b6b3a7640000610f6e565b90505f8560020154826108a491906114c6565b90506108b081846114c6565b6108ba908b6114d9565b995050505050505b50806108cd816114ec565b91505061074b565b5f825f036108f6576040516331ac19c360e11b815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000002386f26fc1000083101561095957604051633e32c46f60e01b81527f000000000000000000000000000000000000000000000000002386f26fc1000060048201526024016104f5565b506001600160a01b0381165f9081526002602052604081208054849291908290610982906114ec565b91829055506001600160a01b0384165f9081526001602052604090209091506109ab908261116f565b506001600160a01b0383165f9081526020818152604080832084845290915290206109ff85670de0b6b3a76400007f0000000000000000000000000000000000000000000000000000000000093a80610f6e565b8155610a2b7f0000000000000000000000000000000000000000000000000000000000093a80426114d9565b426001600160801b03908116600160801b029116176001820155600380548691905f90610a599084906114d9565b925050819055508460045f828254610a7191906114d9565b90915550610aac90506001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21633308861117a565b6040518581526001600160a01b0385169033907f4a73c8b9b5a1c76f8a33e65d5155ab65d9a96b0ae8d7ec834a5678536cebeda79060200160405180910390a3505092915050565b335f9081526001602052604081208190610b0d90610f5b565b80518593509091505f5b5f86118015610b2557508181105b15610cf957335f90815260208190526040812084518290869085908110610b4e57610b4e61149e565b602002602001015181526020019081526020015f2090505f815f0154118015610b9a57505f81600201541180610b9a575060018101546001600160801b03808216600160801b90920416105b15610ce657610bc233858481518110610bb557610bb561149e565b6020026020010151610f8c565b505f8782600201541015610bda578160020154610bdc565b875b9050878103610c065780826002015f828254610bf891906114c6565b909155505f9850610c199050565b610c1081896114c6565b5f600284015597505b866001600160a01b0316336001600160a01b03167f8f7d9ca2df6db2956b58e8476c3d797b62eacd55fe9df5d1cb12aab498333243878681518110610c6057610c6061149e565b602002602001015184604051610c80929190918252602082015260400190565b60405180910390a36002820154158015610ca7575060018201546001600160801b03164210155b15610ce457610ce2858481518110610cc157610cc161149e565b602090810291909101810151335f90815260019092526040909120906110ea565b505b505b5080610cf1816114ec565b915050610b17565b508415610d2657610d0a85846114c6565b6040516303c64f5960e51b81526004016104f591815260200190565b8260035f828254610d3791906114c6565b9091555061071a90506001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21685856110f5565b335f9081526001602052604081208190610d8a90610f5b565b80519091505f5b81811015610eeb57335f90815260208190526040812084518290869085908110610dbd57610dbd61149e565b602002602001015181526020019081526020015f2090505f815f0154118015610e0957505f81600201541180610e09575060018101546001600160801b03808216600160801b90920416105b15610ed857610e2433858481518110610bb557610bb561149e565b506002810154610e3481876114d9565b5f6002840155955060018201546001600160801b03164210610e6757610e65858481518110610cc157610cc161149e565b505b336001600160a01b0316336001600160a01b03167f8f7d9ca2df6db2956b58e8476c3d797b62eacd55fe9df5d1cb12aab498333243878681518110610eae57610eae61149e565b602002602001015184604051610ece929190918252602082015260400190565b60405180910390a3505b5080610ee3816114ec565b915050610d91565b508260035f828254610efd91906114c6565b90915550610f3790506001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21633856110f5565b505090565b6001600160a01b0381165f908152600160205260409020606090610582905b60605f610f6783611200565b9392505050565b828202811515841585830485141716610f85575f80fd5b0492915050565b6001600160a01b0382165f90815260208181526040808320848452909152812080548203610fd05760405163a1632d2b60e01b8152600481018490526024016104f5565b60018101546001600160801b03808216600160801b9092041610610ff7575f915050610582565b60018101545f906001600160801b03164211156110215760018201546001600160801b0316611023565b425b60018301549091505f9061104790600160801b90046001600160801b0316836114c6565b60018401549091506001600160801b0316820361107b578254611074908290670de0b6b3a7640000611259565b9350611094565b8254611091908290670de0b6b3a7640000610f6e565b93505b83836002015f8282546110a791906114d9565b90915550506001830180546001600160801b03808516600160801b029116179055600480548591905f906110dc9084906114c6565b909155505050505092915050565b5f610f678383611286565b5f60405163a9059cbb60e01b815283600482015282602482015260205f6044835f895af13d15601f3d1160015f5114161716915050806111695760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b60448201526064016104f5565b50505050565b5f610f678383611369565b5f6040516323b872dd60e01b815284600482015283602482015282604482015260205f6064835f8a5af13d15601f3d1160015f5114161716915050806111f95760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b60448201526064016104f5565b5050505050565b6060815f0180548060200260200160405190810160405280929190818152602001828054801561124d57602002820191905f5260205f20905b815481526020019060010190808311611239575b50505050509050919050565b828202811515841585830485141716611270575f80fd5b6001826001830304018115150290509392505050565b5f8181526001830160205260408120548015611360575f6112a86001836114c6565b85549091505f906112bb906001906114c6565b905081811461131a575f865f0182815481106112d9576112d961149e565b905f5260205f200154905080875f0184815481106112f9576112f961149e565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061132b5761132b61153a565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050610582565b5f915050610582565b5f8181526001830160205260408120546113ae57508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610582565b505f610582565b80356001600160a01b03811681146113cb575f80fd5b919050565b5f602082840312156113e0575f80fd5b610f67826113b5565b5f80604083850312156113fa575f80fd5b611403836113b5565b946020939093013593505050565b5f8060408385031215611422575f80fd5b50508035926020909101359150565b5f8060408385031215611442575f80fd5b82359150611452602084016113b5565b90509250929050565b602080825282518282018190525f9190848201906040850190845b8181101561149257835183529284019291840191600101611476565b50909695505050505050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b81810381811115610582576105826114b2565b80820180821115610582576105826114b2565b5f600182016114fd576114fd6114b2565b5060010190565b8082028115828204841417610582576105826114b2565b5f8261153557634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52603160045260245ffdfea264697066735822122058211de2cd20b07a68fe0fac8002492e910688cc452e787709b89f56b7c9fc0564736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000002386f26fc10000
-----Decoded View---------------
Arg [0] : _asset (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [1] : _vestingPeriod (uint256): 604800
Arg [2] : _minimumDeposit (uint256): 10000000000000000
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [1] : 0000000000000000000000000000000000000000000000000000000000093a80
Arg [2] : 000000000000000000000000000000000000000000000000002386f26fc10000
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 100.00% | $3,472.79 | 49.8691 | $173,184.98 |
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.