More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 460 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Withdraw | 21628236 | 32 hrs ago | IN | 0 ETH | 0.00034553 | ||||
Withdraw | 21623202 | 2 days ago | IN | 0 ETH | 0.00102412 | ||||
Withdraw | 21586633 | 7 days ago | IN | 0 ETH | 0.00048858 | ||||
Deposit | 21518217 | 16 days ago | IN | 0 ETH | 0.00187309 | ||||
Withdraw | 21517976 | 16 days ago | IN | 0 ETH | 0.00071612 | ||||
Deposit | 21514907 | 17 days ago | IN | 0 ETH | 0.00105536 | ||||
Withdraw | 21454510 | 25 days ago | IN | 0 ETH | 0.00094737 | ||||
Withdraw | 21417264 | 30 days ago | IN | 0 ETH | 0.00296839 | ||||
Withdraw | 21390983 | 34 days ago | IN | 0 ETH | 0.0014142 | ||||
Withdraw | 21372355 | 37 days ago | IN | 0 ETH | 0.00302087 | ||||
Withdraw | 21365619 | 38 days ago | IN | 0 ETH | 0.00412365 | ||||
Withdraw | 21362403 | 38 days ago | IN | 0 ETH | 0.00116493 | ||||
Withdraw | 21348965 | 40 days ago | IN | 0 ETH | 0.00122666 | ||||
Withdraw | 21347673 | 40 days ago | IN | 0 ETH | 0.00110111 | ||||
Withdraw | 21340344 | 41 days ago | IN | 0 ETH | 0.00217159 | ||||
Withdraw | 21325924 | 43 days ago | IN | 0 ETH | 0.00183145 | ||||
Withdraw | 21312188 | 45 days ago | IN | 0 ETH | 0.00170726 | ||||
Withdraw | 21258139 | 53 days ago | IN | 0 ETH | 0.00107411 | ||||
Withdraw | 21226999 | 57 days ago | IN | 0 ETH | 0.00094912 | ||||
Withdraw | 21220677 | 58 days ago | IN | 0 ETH | 0.00084131 | ||||
Withdraw | 21161770 | 66 days ago | IN | 0 ETH | 0.00212873 | ||||
Withdraw | 21107663 | 74 days ago | IN | 0 ETH | 0.00137103 | ||||
Withdraw | 21083048 | 77 days ago | IN | 0 ETH | 0.00090452 | ||||
Withdraw | 21036678 | 83 days ago | IN | 0 ETH | 0.00215695 | ||||
Withdraw | 21026523 | 85 days ago | IN | 0 ETH | 0.00054598 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
AirdropSingleSidedStaking
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 2 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.20; import "./interfaces/IAirdropSingleSidedStaking.sol"; import "./ArcadeSingleSidedStaking.sol"; import { ASS_CallerNotAirdropDistribution } from "../src/errors/SingleSidedStaking.sol"; contract AirdropSingleSidedStaking is IAirdropSingleSidedStaking, ArcadeSingleSidedStaking { address public airdropDistribution; /** * @notice Sets up the contract by initializing the deposit token and the owner. * * @param _owner The address of the contract owner. * @param _arcd The address of the deposit ERC20 token. * @param _airdropDistribution The address of the airdrop distributor contract. */ constructor( address _owner, address _arcd, address _airdropDistribution ) ArcadeSingleSidedStaking(_owner, _arcd) { if (address(_airdropDistribution) == address(0)) revert ASS_ZeroAddress("airdropDistribution"); airdropDistribution = _airdropDistribution; } modifier onlyAirdropDistribution() { if(msg.sender != airdropDistribution) revert ASS_CallerNotAirdropDistribution(); _; } /** @notice Receives an airdrop for a specific recipient with a specified amount, delegation, * and lock period. This function is restricted to be called by the airdrop distribution * account only and will call the internal `_deposit` function to handle the token * transfer and voting power allocation. * * @param recipient The address of the user who will receive the airdropped tokens. * @param amount The amount of tokens that will be airdropped to the user. * @param delegation The address of the user's delegatee. * @param lock The lock period for the airdropped tokens. */ function airdropReceive( address recipient, uint256 amount, address delegation, Lock lock ) external onlyAirdropDistribution { _deposit(recipient, amount, delegation, lock); } /** @notice Sets the airdrop distribution account that is allowed to call `airdropReceive`. * * @param _airdropDistribution The address allowed caller. */ function setAirdropDistribution(address _airdropDistribution) external onlyOwner { airdropDistribution = _airdropDistribution; emit AirdropDistributionSet(airdropDistribution); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.20; import "./IArcadeSingleSidedStaking.sol"; interface IAirdropSingleSidedStaking is IArcadeSingleSidedStaking { event AirdropDistributionSet(address indexed airdropDistribution); function airdropReceive( address recipient, uint256 amount, address delegation, Lock lock ) external; function setAirdropDistribution(address _airdropDistribution) external; function airdropDistribution() external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.20; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import "@openzeppelin/contracts/utils/Pausable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "./external/council/interfaces/IVotingVault.sol"; import "./external/council/libraries/History.sol"; import "./external/council/libraries/Storage.sol"; import "./interfaces/IArcadeSingleSidedStaking.sol"; import { ASS_ZeroAddress, ASS_ZeroAmount, ASS_DepositToken, ASS_BalanceAmount, ASS_Locked, ASS_DepositCountExceeded, ASS_InvalidDelegationAddress, ASS_AmountTooBig } from "../src/errors/SingleSidedStaking.sol"; /** * @title ArcadeSingleSidedStaking * @author Non-Fungible Technologies, Inc. * * @notice To optimize gas usage, unlockTimeStamp in struct UserDeposit is stored in * uint32 format. This limits timestamp support to dates before 03:14:07 UTC on * 19 January 2038. Any time beyond this point will cause an overflow. * * The ArcadeSingleSidedStaking contract is set up like a traditional staking contract, * but with a twist: instead of earning tokens as rewards, users deposit their ARCD tokens * in the contract and get d’App points in return. Earned points are tallied up off-chain * and account towards the $ARCD Rewards program and its Levels. * * Upon depositing, users are required to commit to a lock period where tokens are * immovable, until the chosen lock period expires. Early withdrawal is not permitted. * Users have the flexibility to make multiple deposits, each accruing points separately * until their lock period concludes. * * Should users choose not to withdraw their tokens post the lock period, the * funds will seamlessly transition into a subsequent points tracking cycle if * one should start. Unlike the initial deposit, the funds in the consequent point * tracking cycles are not bound by a lock period and can be freely withdrawn anytime. * Tracking cycles are defined and tracked in the dApp. * * The lock period gives users the opportunity to enhance their points earnings * with bonus multipliers that are contingent on the duration for which the user * chooses to lock their deposited tokens. These bonus calculations are tallied offchain. * The available lock durations are categorized as short, medium, and long. Each category * is associated with a progressively increasing multiplier that enhances the number of * point rewards accrued in the d'App, with the short duration offering the smallest and * the long duration offering the largest. * * In the exitAll() external function, it's necessary to limit the number of * processed transactions within the function's loops to prevent exceeding * the block gas limit. Because of this, the contract enforces a hard limit * on the number deposits a user can have per wallet address and consequently * on the number of iterations that can be processed in a single transaction. * This limit is defined by the MAX_DEPOSITS state variable. Should a user * necessitate making more than the MAX_DEPOSITS number of deposits, they will * be required to use a different wallet address. * * The contract gives users governance capabilities by also serving as a voting * vault. When users deposit, they gain voting power which they can use in * ArcadeDAO governance. Users' voting power is automatically accrued to their account * and is delegated to their chosen delegatee's address on their behalf without the * need for them to call any additional transaction. * The ArcadeSingleSidedStaking contract governance functionality is adapted from the * Council LockingVault deployment at: * https://etherscan.io/address/0x7a58784063D41cb78FBd30d271F047F0b9156d6e#code * * Once a user makes their initial deposit, the voting power for any future deposits * will need to be delegated to the same address as in the initial deposit. To assign * a different delegate, users are required to use the changeDelegate() function. * A user's voting power is determined by the quantity of ARCD tokens they have deposited. */ contract ArcadeSingleSidedStaking is IArcadeSingleSidedStaking, IVotingVault, ReentrancyGuard, Ownable, Pausable { using SafeERC20 for IERC20; // Bring library into scope using History for History.HistoricalBalances; // ============================================ STATE ============================================== // ============== Constants ============== uint256 public constant MAX_DEPOSITS = 20; uint256 public constant SHORT_LOCK_TIME = 30 days; uint256 public constant MEDIUM_LOCK_TIME = 60 days; uint256 public constant LONG_LOCK_TIME = 150 days; // ============ Global State ============= IERC20 public immutable arcd; mapping(address => UserDeposit[]) public deposits; uint256 public totalDeposits; // ========================================== CONSTRUCTOR =========================================== /** * @notice Sets up the contract by initializing the deposit token and setting the owner. * * @param _owner The address of the contract owner. * @param _arcd The address of the deposit ERC20 token. */ constructor( address _owner, address _arcd ) Ownable(_owner) { if (address(_arcd) == address(0)) revert ASS_ZeroAddress("arcd"); arcd = IERC20(_arcd); } // ========================================== VIEW FUNCTIONS ========================================= /** * @notice Returns the total amount of deposited tokens held in the contract. * * @return uint256 The amount of deposited tokens. */ function totalSupply() external view returns (uint256) { return totalDeposits; } /** * @notice Returns the amount of tokens deposited by a user account. * * @param account The address of the account. * * @return userBalance The total amount that the user has deposited. */ function getTotalUserDeposits(address account) external view returns (uint256 userBalance) { UserDeposit[] storage userDeposits = deposits[account]; uint256 numUserDeposits = userDeposits.length; for (uint256 i = 0; i < numUserDeposits; ++i) { UserDeposit storage userDeposit = userDeposits[i]; userBalance += userDeposit.amount; } } /** * @notice Returns the amount of deposited tokens pertaining to a specific deposit. * * @param account The address of the account. * @param depositId The specified deposit to get the balance of. * * @return depositBalance The total amount committed to the deposit. */ function balanceOfDeposit(address account, uint256 depositId) external view returns (uint256 depositBalance) { depositBalance = deposits[account][depositId].amount; } /** * @notice Returns information about a deposit. * @param account The user whose deposit to get. * @param depositId The specified deposit to get. * * @return lock Lock period committed. * @return unlockTimestamp Timestamp marking the end of the lock period. * @return amount Amount deposited. */ function getUserDeposit(address account, uint256 depositId) external view returns (uint8 lock, uint32 unlockTimestamp, uint256 amount) { UserDeposit storage userDeposit = deposits[account][depositId]; lock = uint8(userDeposit.lock); unlockTimestamp = userDeposit.unlockTimestamp; amount = userDeposit.amount; } /** * @notice Returns the last depositId, equivalent to userDeposits.length. * * @param account The user whose last deposit to get. * * @return lastDepositId Id of the last deposit. */ function getLastDepositId(address account) external view returns (uint256 lastDepositId) { lastDepositId = deposits[account].length - 1; } /** * @notice Gets all of a user's active deposits. * * @param account The user whose deposits to get. * * @return activeDeposits Array of id's of the user's active deposits. */ function getActiveDeposits(address account) external view returns (uint256[] memory) { UserDeposit[] storage userDeposits = deposits[account]; uint256 activeCount = 0; uint256 numUserDeposits = userDeposits.length; for (uint256 i = 0; i < numUserDeposits; ++i) { UserDeposit storage userDeposit = userDeposits[i]; if (userDeposit.amount > 0) { activeCount++; } } uint256[] memory activeDeposits = new uint256[](activeCount); uint256 activeIndex; for (uint256 i = 0; i < numUserDeposits; ++i) { if (userDeposits[i].amount > 0) { activeDeposits[activeIndex] = i; activeIndex++; } } return activeDeposits; } // ========================================= MUTATIVE FUNCTIONS ======================================== /** * @notice Allows users to deposit their tokens, which are then tracked in the contract. * * @param amount The amount of tokens the user wishes to deposit and lock. * @param delegation The address to which the user's voting power will be delegated. * @param lock The chosen locking period for the deposited tokens. */ function deposit( uint256 amount, address delegation, Lock lock ) external { _deposit(msg.sender, amount, delegation, lock); } /** * @notice Withdraws deposited tokens that are unlocked. Allows for partial withdrawals. * * @param depositId The specified deposit to withdraw from. * @param amount The amount to be withdrawn. */ function withdraw(uint256 amount, uint256 depositId) public whenNotPaused nonReentrant { if (amount == 0) revert ASS_ZeroAmount(); UserDeposit storage userDeposit = deposits[msg.sender][depositId]; if (userDeposit.amount == 0) revert ASS_BalanceAmount(); if (block.timestamp < userDeposit.unlockTimestamp) revert ASS_Locked(); if (amount > userDeposit.amount) amount = userDeposit.amount; _subtractVotingPower(amount, msg.sender); userDeposit.amount -= amount; totalDeposits -= amount; arcd.safeTransfer(msg.sender, amount); emit Withdrawn(msg.sender, depositId, amount, uint8(userDeposit.lock)); } /** * @notice Allows users to withdraw deposited tokens for a specific deposit * deposit id. Lock period needs to have ended. * * @param depositId The specified deposit to exit. */ function exit(uint256 depositId) external { withdraw(type(uint256).max, depositId); } /** * @notice Allows users to withdraw all their deposited tokens in one transaction. * Lock period needs to have ended. */ function exitAll() external whenNotPaused nonReentrant { UserDeposit[] storage userDeposits = deposits[msg.sender]; uint256 totalWithdrawAmount = 0; uint256 totalVotingPower = 0; uint256 numUserDeposits = userDeposits.length; for (uint256 i = 0; i < numUserDeposits; ++i) { UserDeposit storage userDeposit = userDeposits[i]; uint256 amount = userDeposit.amount; if (amount == 0 || block.timestamp < userDeposit.unlockTimestamp) continue; userDeposit.amount -= amount; totalVotingPower += amount; totalWithdrawAmount += amount; emit Withdrawn(msg.sender, i, amount, uint8(userDeposit.lock)); } if (totalVotingPower > 0) { _subtractVotingPower(totalVotingPower, msg.sender); } if (totalWithdrawAmount > 0) { totalDeposits -= totalWithdrawAmount; arcd.safeTransfer(msg.sender, totalWithdrawAmount); } } // ======================================== RESTRICTED FUNCTIONS ========================================= /** * @notice Allows the contract owner to recover ERC20 tokens locked in the contract. * Deposited ARCD tokens cannot be recovered, they can only be withdrawn * by the depositing user. * * @param tokenAddress The address of the token to recover. * @param tokenAmount The amount of token to recover. */ function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyOwner { if (tokenAddress == address(arcd)) revert ASS_DepositToken(); if (tokenAddress == address(0)) revert ASS_ZeroAddress("token"); if (tokenAmount == 0) revert ASS_ZeroAmount(); IERC20(tokenAddress).safeTransfer(msg.sender, tokenAmount); emit Recovered(tokenAddress, tokenAmount); } /** * @notice Pauses the contract, callable by only the owner. Reversible. */ function pause() external onlyOwner { _pause(); } /** * @notice Unpauses the contract, callable by only the owner. Reversible. */ function unpause() external onlyOwner { _unpause(); } // ============================================== HELPERS =============================================== /** * @notice Internal deposit function updating a user's deposit balance information and voting * power. The total supply of deposited tokens are updated accordingly. * * @param recipient The user whom the deposited tokens are allocated to. * @param amount The amount of tokens to deposit and lock. * @param delegation The address to which the user's voting power will be delegated. * @param lock The locking period for the deposited tokens. */ function _deposit( address recipient, uint256 amount, address delegation, Lock lock ) internal nonReentrant whenNotPaused { if (amount == 0) revert ASS_ZeroAmount(); if (delegation == address(0)) revert ASS_ZeroAddress("delegation"); uint256 userDepositCount = deposits[recipient].length; if (userDepositCount >= MAX_DEPOSITS) revert ASS_DepositCountExceeded(); uint256 lockDuration = _calculateLockDuration(lock); // update the vote power _addVotingPower(recipient, amount, delegation); // populate user deposit information deposits[recipient].push( UserDeposit({ amount: amount, unlockTimestamp: uint32(block.timestamp + lockDuration), lock: lock }) ); totalDeposits += amount; arcd.safeTransferFrom(msg.sender, address(this), amount); emit Deposited(recipient, userDepositCount, amount, uint8(lock)); } /** * @notice Calculates the lock duration for a user's deposit based on the selected lock SHORT, MEDIUM or LONG. * * @param lock The lock period committed. * * @return lockDuration The period duration for the selected lock. */ function _calculateLockDuration(Lock lock) internal pure returns (uint256 lockDuration) { if (lock == Lock.Short) { lockDuration = SHORT_LOCK_TIME; } else if (lock == Lock.Medium) { lockDuration = MEDIUM_LOCK_TIME; } else if (lock == Lock.Long) { lockDuration = LONG_LOCK_TIME; } } /** * @notice This internal function is adapted from the external withdraw function in Council's * LockingVault contract with 2 key modifications: it omits the token transfer transaction * and adds an address account parameter to specify the user whose voting power needs updating. * In the Locking Vault, msg.sender directly indicated the user, whereas in this context, * msg.sender refers to the contract itself. Therefore, we explicitly pass the * user's address. * * @param amount The amount of voting power to subtract. * @param account The account whose voting power to subtract. */ function _subtractVotingPower(uint256 amount, address account) internal { if (amount > type(uint96).max) revert ASS_AmountTooBig(); // Load our deposits storage Storage.AddressUint storage userData = _deposits()[account]; // Reduce the user's stored balance userData.amount -= uint96(amount); address delegate = userData.who; // Reduce the delegate voting power // Get the storage pointer History.HistoricalBalances memory votingPower = _votingPower(); // Load the most recent voter power stamp uint256 delegateeVotes = votingPower.loadTop(delegate); // remove the votes from the delegate votingPower.push(delegate, delegateeVotes - amount); // Emit an event to track votes emit VoteChange(account, delegate, -1 * int256(amount)); } /** * @notice This internal function is adapted from the external deposit function in the Council * LockingVault contract with 2 key modifications: it reverts if the specified delegation * address does not match the user's previously designated delegate, and it no longer * handles token transfers into the contract as these are handled by the deposit function. * * @param fundedAccount The address to credit the voting power to. * @param amount The amount of voting power to add. * @param delegation The user's delegatee address. */ function _addVotingPower( address fundedAccount, uint256 amount, address delegation ) internal { if (amount > type(uint96).max) revert ASS_AmountTooBig(); // No delegating to zero if (delegation == address(0)) revert ASS_ZeroAddress("delegation"); // Load our deposits storage Storage.AddressUint storage userData = _deposits()[fundedAccount]; // Load who has the user's votes address delegate = userData.who; if (delegate == address(0)) { // If the user is un-delegated we delegate to their indicated address delegate = delegation; // Set the delegation userData.who = delegate; } if (delegation != delegate) { revert ASS_InvalidDelegationAddress(); } // Now we increase the user's balance userData.amount += uint96(amount); // Next we increase the delegation to their delegate // Get the storage pointer History.HistoricalBalances memory votingPower = _votingPower(); // Load the most recent voter power stamp uint256 delegateeVotes = votingPower.loadTop(delegate); // Emit an event to track votes emit VoteChange(fundedAccount, delegate, int256(amount)); // Add the newly deposited votes to the delegate votingPower.push(delegate, delegateeVotes + amount); } /** * @notice This function is taken from the Council LockingVault contract. It is a single * endpoint for loading storage for deposits. * * @return A storage mapping which can be used to look * up deposit data. */ function _deposits() internal pure returns (mapping(address => Storage.AddressUint) storage) { // This call returns a storage mapping with a unique non overwrite-able storage location // which can be persisted through upgrades, even if they change storage layout return (Storage.mappingAddressToPackedAddressUint("deposits")); } /** * @notice This function is taken from the Council LockingVault contract. Returns the * historical voting power tracker. * * * @return A struct which can push to and find items in * block indexed storage. */ function _votingPower() internal pure returns (History.HistoricalBalances memory) { // This call returns a storage mapping with a unique non overwrite-able storage location // which can be persisted through upgrades, even if they change storage layout return (History.load("votingPower")); } /** * @notice This function is taken from the Council LockingVault contract. Loads the voting * power of a user. It is revised to no longer clear stale blocks from the queue * in order to avoid gas depletion encountered with overly long queues. * * @param user The address we want to load the voting power of. * @param blockNumber The block number we want the user's voting power at. * * @return The number of votes. */ function queryVotePower( address user, uint256 blockNumber, bytes calldata ) external view override returns (uint256) { return queryVotePowerView(user, blockNumber); } /** * @notice This function is taken from the LockingVault contract. Loads the voting power of a * user without any changes to state. * * @param user The address we want to load the voting power of. * @param blockNumber The block number we want the user's voting power at. * * @return The number of votes. */ function queryVotePowerView(address user, uint256 blockNumber) public view returns (uint256) { // Get our reference to historical data History.HistoricalBalances memory votingPower = _votingPower(); // Find the historical datum return votingPower.find(user, blockNumber); } /** * @notice This function is taken from the Council LockingVault contract, it changes a user's * voting power delegatee. * * @param newDelegate The new address which gets the voting power. */ function changeDelegation(address newDelegate) external { // No delegating to zero if (newDelegate == address(0)) revert ASS_ZeroAddress("delegation"); // Get the stored user data Storage.AddressUint storage userData = _deposits()[msg.sender]; // Get the user balance uint256 userBalance = uint256(userData.amount); address oldDelegate = userData.who; // Reset the user delegation userData.who = newDelegate; // Reduce the old voting power // Get the storage pointer History.HistoricalBalances memory votingPower = _votingPower(); // Load the old delegate's voting power uint256 oldDelegateVotes = votingPower.loadTop(oldDelegate); // Reduce the old voting power votingPower.push(oldDelegate, oldDelegateVotes - userBalance); // Emit an event to track votes emit VoteChange(msg.sender, oldDelegate, -1 * int256(userBalance)); // Get the new delegate's votes uint256 newDelegateVotes = votingPower.loadTop(newDelegate); // Store the increase in power votingPower.push(newDelegate, newDelegateVotes + userBalance); // Emit an event tracking this voting power change emit VoteChange(msg.sender, newDelegate, int256(userBalance)); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.20; /** * @title SingleSidedStaking * @author Non-Fungible Technologies, Inc. * * This file contains all custom errors for the ArcadeSingleSidedStaking contract. * All errors are prefixed by "ASS_" for ArcadeSingleSidedStaking. Errors located in one place * to make it possible to holistically look at all the failure cases. */ // ==================================== Arcade Single Sided Staking Errors ====================================== /** * @notice Zero address passed in where not allowed. * @param addressType The name of the parameter for which a zero * address was provided. */ error ASS_ZeroAddress(string addressType); /** * @notice Cannot withdraw or stake amount zero. */ error ASS_ZeroAmount(); /** * @notice Deposit token cannot be ERC20 recovered. */ error ASS_DepositToken(); /** * @notice User tries to withdraw an amount greater than * than their balance. */ error ASS_BalanceAmount(); /** * @notice Cannot withdraw a deposit which is still locked. */ error ASS_Locked(); /** * @notice Deposits number is larger than MAX_ITERATIONS. * */ error ASS_DepositCountExceeded(); /** * @notice The provided delegate address does not match their initial delegate. */ error ASS_InvalidDelegationAddress(); /** * @notice Amount cannot exceed the maximum value that can be held by a uint96. */ error ASS_AmountTooBig(); // ==================================== Airdrop Single Sided Staking Errors ====================================== /** * @notice The caller is not authorized to make this call. */ error ASS_CallerNotAirdropDistribution();
// SPDX-License-Identifier: MIT pragma solidity 0.8.20; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; interface IArcadeSingleSidedStaking { // ================================================= EVENTS ================================================== event Deposited(address indexed user, uint256 depositId, uint256 amount, uint8 lock); event Withdrawn(address indexed user, uint256 depositId, uint256 amount, uint8 lock); event Recovered(address token, uint256 amount); event VoteChange(address indexed from, address indexed to, int256 amount); // ================================================= STRUCTS ================================================= enum Lock { Short, Medium, Long } struct UserDeposit { Lock lock; uint32 unlockTimestamp; uint256 amount; } // ============================================= VIEW FUNCTIONS ============================================== function getTotalUserDeposits(address account) external view returns (uint256); function totalSupply() external view returns (uint256); function getActiveDeposits(address account) external view returns (uint256[] memory); function getLastDepositId(address account) external view returns (uint256); function getUserDeposit(address account, uint256 depositId) external view returns (uint8 lock, uint32 unlockTimestamp, uint256 amount); function balanceOfDeposit(address account, uint256 depositId) external view returns (uint256); // =========================================== MUTATIVE FUNCTIONS ============================================ function exitAll() external; function exit(uint256 depositId) external; function deposit(uint256 amount, address firstDelegation, Lock lock) external; function withdraw(uint256 amount, uint256 depositId) external; function recoverERC20(address tokenAddress, uint256 tokenAmount) external; function pause() external; function unpause() external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol) pragma solidity ^0.8.20; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant NOT_ENTERED = 1; uint256 private constant ENTERED = 2; uint256 private _status; /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); constructor() { _status = NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be NOT_ENTERED if (_status == ENTERED) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail _status = ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol) pragma solidity ^0.8.20; import {Context} from "../utils/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { bool private _paused; /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); /** * @dev The operation failed because the contract is paused. */ error EnforcedPause(); /** * @dev The operation failed because the contract is not paused. */ error ExpectedPause(); /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { _requireNotPaused(); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { _requirePaused(); _; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { if (paused()) { revert EnforcedPause(); } } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { if (!paused()) { revert ExpectedPause(); } } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {Context} from "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is set to the address provided by the deployer. This can * later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ constructor(address initialOwner) { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.20; interface IVotingVault { /// @notice Attempts to load the voting power of a user /// @param user The address we want to load the voting power of /// @param blockNumber the block number we want the user's voting power at /// @param extraData Abi encoded optional extra data used by some vaults, such as merkle proofs /// @return the number of votes function queryVotePower( address user, uint256 blockNumber, bytes calldata extraData ) external returns (uint256); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.20; import "./Storage.sol"; // This library is an assembly optimized storage library which is designed // to track timestamp history in a struct which uses hash derived pointers. // WARNING - Developers using it should not access the underlying storage // directly since we break some assumptions of high level solidity. Please // note this library also increases the risk profile of memory manipulation // please be cautious in your usage of uninitialized memory structs and other // anti patterns. library History { // The storage layout of the historical array looks like this // [(128 bit min index)(128 bit length)] [0][0] ... [(64 bit block num)(192 bit data)] .... [(64 bit block num)(192 bit data)] // We give the option to the invoker of the search function the ability to clear // stale storage. To find data we binary search for the block number we need // This library expects the blocknumber indexed data to be pushed in ascending block number // order and if data is pushed with the same blocknumber it only retains the most recent. // This ensures each blocknumber is unique and contains the most recent data at the end // of whatever block it indexes [as long as that block is not the current one]. // A struct which wraps a memory pointer to a string and the pointer to storage // derived from that name string by the storage library // WARNING - For security purposes never directly construct this object always use load struct HistoricalBalances { string name; // Note - We use bytes32 to reduce how easy this is to manipulate in high level sol bytes32 cachedPointer; } /// @notice The method by which inheriting contracts init the HistoricalBalances struct /// @param name The name of the variable. Note - these are globals, any invocations of this /// with the same name work on the same storage. /// @return The memory pointer to the wrapper of the storage pointer function load(string memory name) internal pure returns (HistoricalBalances memory) { mapping(address => uint256[]) storage storageData = Storage.mappingAddressToUnit256ArrayPtr(name); bytes32 pointer; assembly { pointer := storageData.slot } return HistoricalBalances(name, pointer); } /// @notice An unsafe method of attaching the cached ptr in a historical balance memory objects /// @param pointer cached pointer to storage /// @return storageData A storage array mapping pointer /// @dev PLEASE DO NOT USE THIS METHOD WITHOUT SERIOUS REVIEW. IF AN EXTERNAL ACTOR CAN CALL THIS WITH // ARBITRARY DATA THEY MAY BE ABLE TO OVERWRITE ANY STORAGE IN THE CONTRACT. function _getMapping(bytes32 pointer) private pure returns (mapping(address => uint256[]) storage storageData) { assembly { storageData.slot := pointer } } /// @notice This function adds a block stamp indexed piece of data to a historical data array /// To prevent duplicate entries if the top of the array has the same blocknumber /// the value is updated instead /// @param wrapper The wrapper which hold the reference to the historical data storage pointer /// @param who The address which indexes the array we need to push to /// @param data The data to append, should be at most 192 bits and will revert if not function push( HistoricalBalances memory wrapper, address who, uint256 data ) internal { // Check preconditions // OoB = Out of Bounds, short for contract bytecode size reduction require(data <= type(uint192).max, "OoB"); // Get the storage this is referencing mapping(address => uint256[]) storage storageMapping = _getMapping(wrapper.cachedPointer); // Get the array we need to push to uint256[] storage storageData = storageMapping[who]; // We load the block number and then shift it to be in the top 64 bits uint256 blockNumber = block.number << 192; // We combine it with the data, because of our require this will have a clean // top 64 bits uint256 packedData = blockNumber | data; // Load the array length (uint256 minIndex, uint256 length) = _loadBounds(storageData); // On the first push we don't try to load uint256 loadedBlockNumber = 0; if (length != 0) { (loadedBlockNumber, ) = _loadAndUnpack(storageData, length - 1); } // The index we push to, note - we use this pattern to not branch the assembly uint256 index = length; // If the caller is changing data in the same block we change the entry for this block // instead of adding a new one. This ensures each block numb is unique in the array. if (loadedBlockNumber == block.number) { index = length - 1; } // We use assembly to write our data to the index assembly { // Stores packed data in the equivalent of storageData[length] sstore( add( // The start of the data slots add(storageData.slot, 1), // index where we store index ), packedData ) } // Reset the boundaries if they changed if (loadedBlockNumber != block.number) { _setBounds(storageData, minIndex, length + 1); } } /// @notice Loads the most recent timestamp of delegation power /// @param wrapper The memory struct which we want to search for historical data /// @param who The user who's balance we want to load /// @return the top slot of the array function loadTop(HistoricalBalances memory wrapper, address who) internal view returns (uint256) { // Load the storage pointer uint256[] storage userData = _getMapping(wrapper.cachedPointer)[who]; // Load the length (, uint256 length) = _loadBounds(userData); // If it's zero no data has ever been pushed so we return zero if (length == 0) { return 0; } // Load the current top (, uint256 storedData) = _loadAndUnpack(userData, length - 1); // and return it return (storedData); } /// @notice Finds the data stored with the highest block number which is less than or equal to a provided /// blocknumber. /// @param wrapper The memory struct which we want to search for historical data /// @param who The address which indexes the array to be searched /// @param blocknumber The blocknumber we want to load the historical data of /// @return The loaded unpacked data at this point in time. function find( HistoricalBalances memory wrapper, address who, uint256 blocknumber ) internal view returns (uint256) { // Get the storage this is referencing mapping(address => uint256[]) storage storageMapping = _getMapping(wrapper.cachedPointer); // Get the array we need to push to uint256[] storage storageData = storageMapping[who]; // Pre load the bounds (uint256 minIndex, uint256 length) = _loadBounds(storageData); // Search for the blocknumber (, uint256 loadedData) = _find(storageData, blocknumber, 0, minIndex, length); // In this function we don't have to change the stored length data return (loadedData); } /// @notice Finds the data stored with the highest blocknumber which is less than or equal to a provided block number /// Opportunistically clears any data older than staleBlock which is possible to clear. /// @param wrapper The memory struct which points to the storage we want to search /// @param who The address which indexes the historical data we want to search /// @param blocknumber The blocknumber we want to load the historical state of /// @param staleBlock A block number which we can [but are not obligated to] delete history older than /// @return The found data function findAndClear( HistoricalBalances memory wrapper, address who, uint256 blocknumber, uint256 staleBlock ) internal returns (uint256) { // Get the storage this is referencing mapping(address => uint256[]) storage storageMapping = _getMapping(wrapper.cachedPointer); // Get the array we need to push to uint256[] storage storageData = storageMapping[who]; // Pre load the bounds (uint256 minIndex, uint256 length) = _loadBounds(storageData); // Search for the blocknumber (uint256 staleIndex, uint256 loadedData) = _find(storageData, blocknumber, staleBlock, minIndex, length); // We clear any data in the stale region // Note - Since find returns 0 if no stale data is found and we use > instead of >= // this won't trigger if no stale data is found. Plus it won't trigger on minIndex == staleIndex // == maxIndex and clear the whole array. if (staleIndex > minIndex) { // Delete the outdated stored info _clear(minIndex, staleIndex, storageData); // Reset the array info with stale index as the new minIndex _setBounds(storageData, staleIndex, length); } return (loadedData); } /// @notice Searches for the data stored at the largest blocknumber index less than a provided parameter. /// Allows specification of a expiration stamp and returns the greatest examined index which is /// found to be older than that stamp. /// @param data The stored data /// @param blocknumber the blocknumber we want to load the historical data for. /// @param staleBlock The oldest block that we care about the data stored for, all previous data can be deleted /// @param startingMinIndex The smallest filled index in the array /// @param length the length of the array /// @return Returns the largest stale data index seen or 0 for no seen stale data and the stored data function _find( uint256[] storage data, uint256 blocknumber, uint256 staleBlock, uint256 startingMinIndex, uint256 length ) private view returns (uint256, uint256) { // We explicitly revert on the reading of memory which is uninitialized require(length != 0, "uninitialized"); // Do some correctness checks require(staleBlock <= blocknumber); require(startingMinIndex < length); // Load the bounds of our binary search uint256 maxIndex = length - 1; uint256 minIndex = startingMinIndex; uint256 staleIndex = 0; // We run a binary search on the block number fields in the array between // the minIndex and maxIndex. If we find indexes with blocknumber < staleBlock // we set staleIndex to them and return that data for an optional clearing step // in the calling function. while (minIndex != maxIndex) { // We use the ceil instead of the floor because this guarantees that // we pick the highest blocknumber less than or equal the requested one uint256 mid = (minIndex + maxIndex + 1) / 2; // Load and unpack the data in the midpoint index (uint256 pastBlock, uint256 loadedData) = _loadAndUnpack(data, mid); // If we've found the exact block we are looking for if (pastBlock == blocknumber) { // Then we just return the data return (staleIndex, loadedData); // Otherwise if the loaded block is smaller than the block number } else if (pastBlock < blocknumber) { // Then we first check if this is possibly a stale block if (pastBlock < staleBlock) { // If it is we mark it for clearing staleIndex = mid; } // We then repeat the search logic on the indices greater than the midpoint minIndex = mid; // In this case the pastBlock > blocknumber } else { // We then repeat the search on the indices below the midpoint maxIndex = mid - 1; } } // We load at the final index of the search (uint256 _pastBlock, uint256 _loadedData) = _loadAndUnpack(data, minIndex); // This will only be hit if a user has misconfigured the stale index and then // tried to load father into the past than has been preserved require(_pastBlock <= blocknumber, "Search Failure"); return (staleIndex, _loadedData); } /// @notice Clears storage between two bounds in array /// @param oldMin The first index to set to zero /// @param newMin The new minimum filled index, ie clears to index < newMin /// @param data The storage array pointer function _clear( uint256 oldMin, uint256 newMin, uint256[] storage data ) private { // Correctness checks on this call require(oldMin <= newMin); // This function is private and trusted and should be only called by functions which ensure // that oldMin < newMin < length assembly { // The layout of arrays in solidity is [length][data]....[data] so this pointer is the // slot to write to data let dataLocation := add(data.slot, 1) // Loop through each index which is below new min and clear the storage // Note - Uses strict min so if given an input like oldMin = 5 newMin = 5 will be a no op for { let i := oldMin } lt(i, newMin) { i := add(i, 1) } { // store at the starting data pointer + i 256 bits of zero sstore(add(dataLocation, i), 0) } } } /// @notice Loads and unpacks the block number index and stored data from a data array /// @param data the storage array /// @param i the index to load and unpack /// @return (block number, stored data) function _loadAndUnpack(uint256[] storage data, uint256 i) private view returns (uint256, uint256) { // This function is trusted and should only be called after checking data lengths // we use assembly for the sload to avoid reloading length. uint256 loaded; assembly { loaded := sload(add(add(data.slot, 1), i)) } // Unpack the packed 64 bit block number and 192 bit data field return ( loaded >> 192, loaded & 0x0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff ); } /// @notice This function sets our non standard bounds data field where a normal array /// would have length /// @param data the pointer to the storage array /// @param minIndex The minimum non stale index /// @param length The length of the storage array function _setBounds( uint256[] storage data, uint256 minIndex, uint256 length ) private { // Correctness check require(minIndex < length); assembly { // Ensure data cleanliness let clearedLength := and( length, 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff ) // We move the min index into the top 128 bits by shifting it left by 128 bits let minInd := shl(128, minIndex) // We pack the data using binary or let packed := or(minInd, clearedLength) // We store in the packed data in the length field of this storage array sstore(data.slot, packed) } } /// @notice This function loads and unpacks our packed min index and length for our custom storage array /// @param data The pointer to the storage location /// @return minInd the first filled index in the array /// @return length the length of the array function _loadBounds(uint256[] storage data) private view returns (uint256 minInd, uint256 length) { // Use assembly to manually load the length storage field uint256 packedData; assembly { packedData := sload(data.slot) } // We use a shift right to clear out the low order bits of the data field minInd = packedData >> 128; // We use a binary and to extract only the bottom 128 bits length = packedData & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff; } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.20; // This library allows for secure storage pointers across proxy implementations // It will return storage pointers based on a hashed name and type string. library Storage { // This library follows a pattern which if solidity had higher level // type or macro support would condense quite a bit. // Each basic type which does not support storage locations is encoded as // a struct of the same name capitalized and has functions 'load' and 'set' // which load the data and set the data respectively. // All types will have a function of the form 'typename'Ptr('name') -> storage ptr // which will return a storage version of the type with slot which is the hash of // the variable name and type string. This pointer allows easy state management between // upgrades and overrides the default solidity storage slot system. /// @dev The address type container struct Address { address data; } /// @notice A function which turns a variable name for a storage address into a storage /// pointer for its container. /// @param name the variable name /// @return data the storage pointer function addressPtr(string memory name) internal pure returns (Address storage data) { bytes32 typehash = keccak256("address"); bytes32 offset = keccak256(abi.encodePacked(typehash, name)); assembly { data.slot := offset } } /// @notice A function to load an address from the container struct /// @param input the storage pointer for the container /// @return the loaded address function load(Address storage input) internal view returns (address) { return input.data; } /// @notice A function to set the internal field of an address container /// @param input the storage pointer to the container /// @param to the address to set the container to function set(Address storage input, address to) internal { input.data = to; } /// @dev The uint256 type container struct Uint256 { uint256 data; } /// @notice A function which turns a variable name for a storage uint256 into a storage /// pointer for its container. /// @param name the variable name /// @return data the storage pointer function uint256Ptr(string memory name) internal pure returns (Uint256 storage data) { bytes32 typehash = keccak256("uint256"); bytes32 offset = keccak256(abi.encodePacked(typehash, name)); assembly { data.slot := offset } } /// @notice A function to load an uint256 from the container struct /// @param input the storage pointer for the container /// @return the loaded uint256 function load(Uint256 storage input) internal view returns (uint256) { return input.data; } /// @notice A function to set the internal field of a unit256 container /// @param input the storage pointer to the container /// @param to the address to set the container to function set(Uint256 storage input, uint256 to) internal { input.data = to; } /// @notice Returns the storage pointer for a named mapping of address to uint256 /// @param name the variable name for the pointer /// @return data the mapping pointer function mappingAddressToUnit256Ptr(string memory name) internal pure returns (mapping(address => uint256) storage data) { bytes32 typehash = keccak256("mapping(address => uint256)"); bytes32 offset = keccak256(abi.encodePacked(typehash, name)); assembly { data.slot := offset } } /// @notice Returns the storage pointer for a named mapping of address to uint256[] /// @param name the variable name for the pointer /// @return data the mapping pointer function mappingAddressToUnit256ArrayPtr(string memory name) internal pure returns (mapping(address => uint256[]) storage data) { bytes32 typehash = keccak256("mapping(address => uint256[])"); bytes32 offset = keccak256(abi.encodePacked(typehash, name)); assembly { data.slot := offset } } /// @notice Allows external users to calculate the slot given by this lib /// @param typeString the string which encodes the type /// @param name the variable name /// @return the slot assigned by this lib function getPtr(string memory typeString, string memory name) external pure returns (uint256) { bytes32 typehash = keccak256(abi.encodePacked(typeString)); bytes32 offset = keccak256(abi.encodePacked(typehash, name)); return (uint256)(offset); } // A struct which represents 1 packed storage location with a compressed // address and uint96 pair struct AddressUint { address who; uint96 amount; } /// @notice Returns the storage pointer for a named mapping of address to uint256[] /// @param name the variable name for the pointer /// @return data the mapping pointer function mappingAddressToPackedAddressUint(string memory name) internal pure returns (mapping(address => AddressUint) storage data) { bytes32 typehash = keccak256("mapping(address => AddressUint)"); bytes32 offset = keccak256(abi.encodePacked(typehash, name)); assembly { data.slot := offset } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "./IERC20.sol"; import {IERC20Metadata} from "./extensions/IERC20Metadata.sol"; import {Context} from "../../utils/Context.sol"; import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * The default value of {decimals} is 18. To change this, you should override * this function so it returns a different value. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. */ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { mapping(address account => uint256) private _balances; mapping(address account => mapping(address spender => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the default value returned by this function, unless * it's overridden. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `value`. */ function transfer(address to, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _transfer(owner, to, value); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, value); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `value`. * - the caller must have allowance for ``from``'s tokens of at least * `value`. */ function transferFrom(address from, address to, uint256 value) public virtual returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, value); _transfer(from, to, value); return true; } /** * @dev Moves a `value` amount of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _transfer(address from, address to, uint256 value) internal { if (from == address(0)) { revert ERC20InvalidSender(address(0)); } if (to == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(from, to, value); } /** * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding * this function. * * Emits a {Transfer} event. */ function _update(address from, address to, uint256 value) internal virtual { if (from == address(0)) { // Overflow check required: The rest of the code assumes that totalSupply never overflows _totalSupply += value; } else { uint256 fromBalance = _balances[from]; if (fromBalance < value) { revert ERC20InsufficientBalance(from, fromBalance, value); } unchecked { // Overflow not possible: value <= fromBalance <= totalSupply. _balances[from] = fromBalance - value; } } if (to == address(0)) { unchecked { // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply. _totalSupply -= value; } } else { unchecked { // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256. _balances[to] += value; } } emit Transfer(from, to, value); } /** * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0). * Relies on the `_update` mechanism * * Emits a {Transfer} event with `from` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _mint(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(address(0), account, value); } /** * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply. * Relies on the `_update` mechanism. * * Emits a {Transfer} event with `to` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead */ function _burn(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidSender(address(0)); } _update(account, address(0), value); } /** * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. * * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. */ function _approve(address owner, address spender, uint256 value) internal { _approve(owner, spender, value, true); } /** * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event. * * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any * `Approval` event during `transferFrom` operations. * * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to * true using the following override: * ``` * function _approve(address owner, address spender, uint256 value, bool) internal virtual override { * super._approve(owner, spender, value, true); * } * ``` * * Requirements are the same as {_approve}. */ function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { if (owner == address(0)) { revert ERC20InvalidApprover(address(0)); } if (spender == address(0)) { revert ERC20InvalidSpender(address(0)); } _allowances[owner][spender] = value; if (emitEvent) { emit Approval(owner, spender, value); } } /** * @dev Updates `owner` s allowance for `spender` based on spent `value`. * * Does not update the allowance value in case of infinite allowance. * Revert if not enough allowance is available. * * Does not emit an {Approval} event. */ function _spendAllowance(address owner, address spender, uint256 value) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { if (currentAllowance < value) { revert ERC20InsufficientAllowance(spender, currentAllowance, value); } unchecked { _approve(owner, spender, currentAllowance - value, false); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert FailedInnerCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol) pragma solidity ^0.8.20; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol) pragma solidity ^0.8.20; /** * @dev Standard ERC20 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens. */ interface IERC20Errors { /** * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. * @param balance Current balance for the interacting account. * @param needed Minimum amount required to perform a transfer. */ error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC20InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC20InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. * @param spender Address that may be allowed to operate on tokens without being their owner. * @param allowance Amount of tokens a `spender` is allowed to operate with. * @param needed Minimum amount required to perform a transfer. */ error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC20InvalidApprover(address approver); /** * @dev Indicates a failure with the `spender` to be approved. Used in approvals. * @param spender Address that may be allowed to operate on tokens without being their owner. */ error ERC20InvalidSpender(address spender); } /** * @dev Standard ERC721 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens. */ interface IERC721Errors { /** * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20. * Used in balance queries. * @param owner Address of the current owner of a token. */ error ERC721InvalidOwner(address owner); /** * @dev Indicates a `tokenId` whose `owner` is the zero address. * @param tokenId Identifier number of a token. */ error ERC721NonexistentToken(uint256 tokenId); /** * @dev Indicates an error related to the ownership over a particular token. Used in transfers. * @param sender Address whose tokens are being transferred. * @param tokenId Identifier number of a token. * @param owner Address of the current owner of a token. */ error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC721InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC721InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `operator`’s approval. Used in transfers. * @param operator Address that may be allowed to operate on tokens without being their owner. * @param tokenId Identifier number of a token. */ error ERC721InsufficientApproval(address operator, uint256 tokenId); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC721InvalidApprover(address approver); /** * @dev Indicates a failure with the `operator` to be approved. Used in approvals. * @param operator Address that may be allowed to operate on tokens without being their owner. */ error ERC721InvalidOperator(address operator); } /** * @dev Standard ERC1155 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens. */ interface IERC1155Errors { /** * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. * @param balance Current balance for the interacting account. * @param needed Minimum amount required to perform a transfer. * @param tokenId Identifier number of a token. */ error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC1155InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC1155InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `operator`’s approval. Used in transfers. * @param operator Address that may be allowed to operate on tokens without being their owner. * @param owner Address of the current owner of a token. */ error ERC1155MissingApprovalForAll(address operator, address owner); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC1155InvalidApprover(address approver); /** * @dev Indicates a failure with the `operator` to be approved. Used in approvals. * @param operator Address that may be allowed to operate on tokens without being their owner. */ error ERC1155InvalidOperator(address operator); /** * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation. * Used in batch transfers. * @param idsLength Length of the array of token identifiers * @param valuesLength Length of the array of token amounts */ error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength); }
{ "remappings": [ "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/" ], "optimizer": { "enabled": true, "runs": 2 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "viaIR": true, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_arcd","type":"address"},{"internalType":"address","name":"_airdropDistribution","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ASS_AmountTooBig","type":"error"},{"inputs":[],"name":"ASS_BalanceAmount","type":"error"},{"inputs":[],"name":"ASS_CallerNotAirdropDistribution","type":"error"},{"inputs":[],"name":"ASS_DepositCountExceeded","type":"error"},{"inputs":[],"name":"ASS_DepositToken","type":"error"},{"inputs":[],"name":"ASS_InvalidDelegationAddress","type":"error"},{"inputs":[],"name":"ASS_Locked","type":"error"},{"inputs":[{"internalType":"string","name":"addressType","type":"string"}],"name":"ASS_ZeroAddress","type":"error"},{"inputs":[],"name":"ASS_ZeroAmount","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"airdropDistribution","type":"address"}],"name":"AirdropDistributionSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"lock","type":"uint8"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"int256","name":"amount","type":"int256"}],"name":"VoteChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"lock","type":"uint8"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"LONG_LOCK_TIME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_DEPOSITS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MEDIUM_LOCK_TIME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SHORT_LOCK_TIME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"airdropDistribution","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"delegation","type":"address"},{"internalType":"enum IArcadeSingleSidedStaking.Lock","name":"lock","type":"uint8"}],"name":"airdropReceive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"arcd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"balanceOfDeposit","outputs":[{"internalType":"uint256","name":"depositBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newDelegate","type":"address"}],"name":"changeDelegation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"delegation","type":"address"},{"internalType":"enum IArcadeSingleSidedStaking.Lock","name":"lock","type":"uint8"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"deposits","outputs":[{"internalType":"enum IArcadeSingleSidedStaking.Lock","name":"lock","type":"uint8"},{"internalType":"uint32","name":"unlockTimestamp","type":"uint32"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"exit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"exitAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getActiveDeposits","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getLastDepositId","outputs":[{"internalType":"uint256","name":"lastDepositId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getTotalUserDeposits","outputs":[{"internalType":"uint256","name":"userBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"getUserDeposit","outputs":[{"internalType":"uint8","name":"lock","type":"uint8"},{"internalType":"uint32","name":"unlockTimestamp","type":"uint32"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"queryVotePower","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"queryVotePowerView","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_airdropDistribution","type":"address"}],"name":"setAirdropDistribution","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalDeposits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60a034620001ab57601f62001f9738819003918201601f191683019291906001600160401b03841183851017620001b0578160609284926040968752833981010312620001ab576200005181620001c6565b906200006d836200006560208401620001c6565b9201620001c6565b60016000556001600160a01b039290919083169081156200019357839060015492865193818482167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a36001600160a81b03191617600155169081156200016957506080521680156200012457600480546001600160a01b03191691909117905551611dbb9081620001dc823960805181818161076d015281816109f101528181610efa0152818161112601526113b90152f35b8151635a8af66b60e01b815260206004820152601360248201527f61697264726f70446973747269627574696f6e000000000000000000000000006044820152606490fd5b606490635a8af66b60e01b8152602060048201526004602482015263185c98d960e21b6044820152fd5b8451631e4fbdf760e01b815260006004820152602490fd5b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620001ab5756fe6080604081815260048036101561001557600080fd5b600092833560e01c9081630f0cb0b9146114245750806318160ddd14610f865780632cbb43fc146114065780632d984723146113e8578063375c22cd146113a45780633f4ba83a14611335578063441a3e701461128d578063506384171461121b5780635b6c508c146110ba5780635c975abb14611093578063600381f0146110755780636071105814610ff3578063715018a614610fa55780637d88209714610f865780637f8661a114610e6257806381a4fdd814610e1a5780638456cb5914610db957806387cef13014610aae5780638980f11f146109bb5780638da5cb5b146109925780639f973fd5146108ab578063b7ec8d4b1461054e578063d2d48ee3146103f0578063d6d681771461035f578063de1b905214610343578063deac03011461031c578063e7d20283146102f2578063e91f323514610253578063ea32f42e146101e75763f2fde38b1461016d57600080fd5b346101e35760203660031901126101e35761018661147c565b9061018f6114c9565b6001600160a01b039182169283156101cd575050600180546001600160a01b03198116841790915516600080516020611d668339815191528380a380f35b51631e4fbdf760e01b8152908101849052602490fd5b8280fd5b83823461024f57602036600319011261024f5761020261147c565b61020a6114c9565b81546001600160a01b0319166001600160a01b03919091169081179091557fc35d72a054acf727dbcb414ab4b300b86c00481ccf567ab027bd57a0fb7727828280a280f35b5080fd5b50346101e35760603660031901126101e35761026d61147c565b90604435906001600160401b03908183116102ee57366023840112156102ee578201359081116102ea57369101602401116101e357816020936102e292856102b361190a565b01516001600160a01b03909116825285522080546001600160801b0381169160809190911c9060243590611b4c565b915191825250f35b8480fd5b8580fd5b50503461024f578060031936011261024f576102e28160209361031361147c565b856102b361190a565b50346101e357826003193601126101e3575490516001600160a01b03909116815260209150f35b50503461024f578160031936011261024f576020905160148152f35b5091903461024f578060031936011261024f5761037a61147c565b6001600160a01b031682526002602052808220805460243591908210156103ec57906103a591611497565b50908154600160ff82169301549282519460038210156103d95750845260081c63ffffffff16602084015282015260609150f35b634e487b7160e01b815260218752602490fd5b8380fd5b50346101e3576020806003193601126103ec57926001600160a01b0361041461147c565b168152600284528281209181908354835b81811061050e575061044e610439846115be565b936104468851958661159b565b8085526115be565b8388019590601f19013687378490855b8381106104a557505050505083519485948186019282875251809352850193925b82811061048e57505050500390f35b83518552869550938101939281019260010161047f565b60016104b782849c99989a9b9c611497565b5001546104d3575b6104c8906114f5565b98979694959861045e565b9187518110156104fb576104f38184896104c89460051b8c0101526114f5565b9290506104bf565b634e487b7160e01b865260328552602486fd5b600161051e82889a979698611497565b500154610539575b61052f906114f5565b9694929396610425565b9461054661052f916114f5565b959050610526565b5091903461024f57606036600319011261024f5760249283356001600160a01b038181169383359290918582036108a7576044359460038610156108a35761059461179e565b61059c61177d565b84156108955786156108775733885260209260028452848920549760148910156108675789918289610839575062278d00945b6001600160601b0390818a11610829576105ef6105ea6118cc565b611c8e565b8d3390528852888d2090815485828216958615610810575b505050831680940361080057908c969594939291818b16815460a01c019182116107ee5761067593929161063a916117e6565b61066f8a61064661190a565b936106518486611afd565b908c51838152600080516020611d268339815191528d3392a361151a565b916119c7565b3384526002855263ffffffff61068e878620944261151a565b169086519061069c82611534565b6107dc5788815285810191825286810188815284549094600160401b8210156107ca57906106cf91600182018155611497565b9590956107b95750519160038310156107a7575092600161079d969593600080516020611d468339815191529a9b9c936107919664ffffffff0060ff86549316918260ff1985161787555160081b169164ffffffffff1916171783555191015561073b8660035461151a565b6003558351916323b872dd60e01b9083015233908201523060448201528460648201526064815261076b81611580565b7f0000000000000000000000000000000000000000000000000000000000000000611647565b519283923396846115d5565b0390a26001815580f35b634e487b7160e01b8b52602190528a8afd5b634e487b7160e01b81528084528c90fd5b634e487b7160e01b8752604185528d87fd5b634e487b7160e01b8552602183528b85fd5b634e487b7160e01b8852601186528e88fd5b885163be6422b760e01b81528690fd5b6001600160a01b03199092161783559350388581610607565b885163034c196960e31b81528690fd5b8b935060018a0361084f5750624f1a00946105cf565b948b93506001198a016105cf5762c5c10095506105cf565b85516308f0bb4160e21b81528390fd5b8351635a8af66b60e01b81529081906108919082016117c1565b0390fd5b8351633e9842ab60e01b8152fd5b8780fd5b8680fd5b5090346101e35760203660031901126101e3576108c661147c565b6001600160a01b0381811693909290841561097857506108e76105ea6118cc565b33865260205280852080546001600160a01b03198116861790915560a081901c93169161096c9061091661190a565b9061093461092d876109288886611afd565b611527565b86846119c7565b61093d86611809565b948451958652600080516020611d26833981519152958660203392a361066f866109678385611afd565b61151a565b5191825260203392a380f35b9051635a8af66b60e01b81529081906108919082016117c1565b50503461024f578160031936011261024f5760015490516001600160a01b039091168152602090f35b5082903461024f578260031936011261024f576109d661147c565b602435916109e26114c9565b6001600160a01b0382811691907f0000000000000000000000000000000000000000000000000000000000000000168214610aa0578115610a74578315610a6657507f8c1256b8896378cd5044f80c202f9772b9d77dc85c8a6eb51967210b09bfaa289394610a5684610a6093339061160b565b51928392836115f0565b0390a180f35b8551633e9842ab60e01b8152fd5b6064906020875191635a8af66b60e01b835282015260056024820152643a37b5b2b760d91b6044820152fd5b85516305c6159560e31b8152fd5b5082903461024f57608036600319011261024f57610aca61147c565b602493843591604435916001600160a01b03808416908185036108a357606435936003851015610db557818854163303610da557610b0661179e565b610b0e61177d565b8615610d95578215610d7d5781169687895260209260028452848a2054966014881015610867578a918288610d4f575062278d00945b6001600160601b0390818c1161082957888e8e610b626105ea6118cc565b9082528a5220908154818116958615610d36575b5050841680930361080057908d969594939291818d16815460a01c01918211610d24578c8a8f94610bae610bdc979561066f956117e6565b610bb661190a565b95600080516020611d268339815191528d610bd1888a611afd565b9451868152a361151a565b8984526002855263ffffffff610bf5878620944261151a565b1690865190610c0382611534565b610d12578781528581019182528681018a815284549094600160401b821015610d005790610c3691600182018155611497565b959095610cef575051916003831015610cdd575092600161079d97969593600080516020611d468339815191529b9c9d93610cd39664ffffffff0060ff86549316918260ff1985161787555160081b169164ffffffffff19161717835551910155610ca38860035461151a565b6003558351916323b872dd60e01b9083015233908201523060448201528660648201526064815261076b81611580565b51938493846115d5565b634e487b7160e01b8c52602190528b8bfd5b634e487b7160e01b81528084528d90fd5b634e487b7160e01b8752604185528e87fd5b634e487b7160e01b8552602183528c85fd5b634e487b7160e01b8852601186528f88fd5b6001600160a01b03199091168517835594503880610b76565b8c935060018903610d655750624f1a0094610b44565b948c93506001198901610b445762c5c1009550610b44565b8351635a8af66b60e01b815280610891818b016117c1565b8351633e9842ab60e01b81528890fd5b83516303b22c3f60e51b81528890fd5b8880fd5b50503461024f578160031936011261024f5760207f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25891610df76114c9565b610dff61177d565b6001805460ff60a01b1916600160a01b17905551338152a180f35b50503461024f578060031936011261024f57602091600190610e58906001600160a01b03610e4661147c565b16815260028552836024359120611497565b5001549051908152f35b5091903461024f57602036600319011261024f57823590610e8161177d565b610e8961179e565b6000193384526002602052610ea083838620611497565b5090600182019182548015610f765763ffffffff825460081c164210610f6757808311610f5e575b5060ff9192610ed7338561182a565b610ee2848254611527565b9055610ef083600354611527565b600355610f1e83337f000000000000000000000000000000000000000000000000000000000000000061160b565b5416916003831015610f4b579061079d600080516020611d068339815191529392519283923396846115d5565b634e487b7160e01b855260218652602485fd5b915060ff610ec8565b845162b4ffe560e41b81528890fd5b845163460e84d760e11b81528890fd5b50503461024f578160031936011261024f576020906003549051908152f35b8334610ff05780600319360112610ff057610fbe6114c9565b600180546001600160a01b0319811690915581906001600160a01b0316600080516020611d668339815191528280a380f35b80fd5b5091903461024f578060031936011261024f576001600160a01b0361101661147c565b168252600260205261102c602435828420611497565b509081549160ff8316936003851015611062575060609450600163ffffffff91015492825194855260081c166020840152820152f35b634e487b7160e01b815260218652602490fd5b50503461024f578160031936011261024f5760209051624f1a008152f35b50503461024f578160031936011261024f5760209060ff60015460a01c1690519015158152f35b5090346101e357826003193601126101e357906110d561177d565b6110dd61179e565b338352600260205281832080548493849392845b8381106111615787868880611150575b5080611110575b506001815580f35b8061112061114a92600354611527565b600355337f000000000000000000000000000000000000000000000000000000000000000061160b565b81611108565b61115b90339061182a565b82611101565b61116b8183611497565b5096600188019081549182158015611208575b6111fa578261119f8160ff946111a5946111988380611527565b905561151a565b9961151a565b98541660038110156111e75790600080516020611d068339815191526111d76111df94938751918291339587846115d5565b0390a26114f5565b9594956110f1565b634e487b7160e01b8a526021875260248afd5b50969750506111df906114f5565b5063ffffffff8a5460081c16421061117e565b50503461024f57602036600319011261024f5761123661147c565b6001600160a01b03168252600260205280822080549092919082905b808210611263576020848451908152f35b90926112816112879160016112788789611497565b5001549061151a565b936114f5565b90611252565b5091903461024f578060031936011261024f576024359083356112ae61177d565b6112b661179e565b8081156113255733855260026020526112d184848720611497565b50600181019283549081156113155763ffffffff835460081c164210611306578110610f5e575060ff9192610ed7338561182a565b855162b4ffe560e41b81528990fd5b855163460e84d760e11b81528990fd5b8251633e9842ab60e01b81528690fd5b50346101e357826003193601126101e35761134e6114c9565b6001549060ff8260a01c1615611396575060ff60a01b1916600155513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa90602090a180f35b8251638dfc202b60e01b8152fd5b50503461024f578160031936011261024f57517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50503461024f578160031936011261024f576020905162278d008152f35b50503461024f578160031936011261024f576020905162c5c1008152f35b905083833461024f57602036600319011261024f576001600160a01b0361144961147c565b168252600260205281205460001981019190821161146957602083838152f35b634e487b7160e01b815260118452602490fd5b600435906001600160a01b038216820361149257565b600080fd5b80548210156114b35760005260206000209060011b0190600090565b634e487b7160e01b600052603260045260246000fd5b6001546001600160a01b031633036114dd57565b60405163118cdaa760e01b8152336004820152602490fd5b60001981146115045760010190565b634e487b7160e01b600052601160045260246000fd5b9190820180921161150457565b9190820391821161150457565b606081019081106001600160401b0382111761154f57604052565b634e487b7160e01b600052604160045260246000fd5b604081019081106001600160401b0382111761154f57604052565b60a081019081106001600160401b0382111761154f57604052565b601f909101601f19168101906001600160401b0382119082101761154f57604052565b6001600160401b03811161154f5760051b60200190565b91604091949360ff9160608501968552602085015216910152565b6001600160a01b039091168152602081019190915260400190565b611640611645939261163260405194859263a9059cbb60e01b6020850152602484016115f0565b03601f19810184528361159b565b611647565b565b60018060a01b031690600080826020829451910182865af13d1561170e573d906001600160401b0382116116fa57906116a291604051916116926020601f19601f840116018461159b565b82523d84602084013e5b8461171a565b9081519182151592836116d2575b5050506116ba5750565b60249060405190635274afe760e01b82526004820152fd5b81929350906020918101031261024f576020015190811591821503610ff057503880806116b0565b634e487b7160e01b83526041600452602483fd5b6116a29060609061169c565b90611741575080511561172f57805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580611774575b611752575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b1561174a565b60ff60015460a01c1661178c57565b60405163d93c066560e01b8152600490fd5b6002600054146117af576002600055565b604051633ee5aeb560e01b8152600490fd5b60609060208152600a6020820152693232b632b3b0ba34b7b760b11b60408201520190565b80546001600160a01b031660a09290921b6001600160a01b031916919091179055565b60008190039190600160ff1b81146001166115045760001983050361150457565b906001600160601b03908183116118ba576118466105ea6118cc565b9160018060a01b038092169283600052602052604060002090808516825460a01c0390811161150457600080516020611d26833981519152928261188f6118b1936020956117e6565b5416946118ac61189d61190a565b8761066f846109288385611afd565b611809565b604051908152a3565b60405163034c196960e31b8152600490fd5b604051906118d982611565565b60088252676465706f7369747360c01b6020830152565b604051906118fd82611565565b6000602083606081520152565b6119126118f0565b50604080519061192182611565565b600b825260206a3b37ba34b733a837bbb2b960a91b818401526119426118f0565b508151818101907f7b1a68ec3e3284b167e69db1c622dcfa612281976b71d7e2d239dbe16a75891a8252845184828560005b8481106119ad575050611996928101600083820152038581018452018261159b565b5190209151926119a584611565565b835282015290565b819350809250890101518782860101520182858792611974565b9091906001600160c01b038211611ad2576020908101516001600160a01b039093166000908152929052604082208054909290916001600160801b0391828416918183611aa1575b839043149182611a79575b4360c01b179087016001015515611a33575b5050505050565b60018201809211611a6557818460801c1015610ff057506001600160801b031990921691161790553880808080611a2c565b634e487b7160e01b81526011600452602490fd5b90506000198401848111611a8d5790611a1a565b634e487b7160e01b84526011600452602484fd5b506000198301838111611abe57611ab89087611c75565b50611a0f565b634e487b7160e01b83526011600452602483fd5b60405162461bcd60e51b815260206004820152600360248201526227b7a160e91b6044820152606490fd5b6020908101516001600160a01b0390921660009081529190526040902080546001600160801b03168015611b4557600019810190811161150457611b4091611c75565b905090565b5050600090565b919093928015611c405780821015611492576000199080820190811161150457919291905b818403611bc657505090611b8491611c75565b9210611b905760009190565b60405162461bcd60e51b815260206004820152600e60248201526d536561726368204661696c75726560901b6044820152606490fd5b611bd28285979561151a565b6001808201809211611c2b571c611be98185611c75565b9790868103611bff575050505050505060009190565b8698509590919293949510600014611c1d5750915b90929192611b71565b925081810190811115611c14575b60246000634e487b7160e01b81526011600452fd5b60405162461bcd60e51b815260206004820152600d60248201526c1d5b9a5b9a5d1a585b1a5e9959609a1b6044820152606490fd5b016001015460c081901c916001600160c01b0390911690565b604090815190602092838301937f03a912cdb153207069d92d44a2357e3f0ce00f7ee84da3510f1c6851b4cac4ee85528383516000945b818610611ced5750611ce794508101600084820152039081018452018261159b565b51902090565b85810184015187870186015294830194869250611cc556fe939e45c2a0bfec7a2bdfa3cb3e08e1bc7f8ba9dc5fe33aac70c75c949ebbb62433161cf2da28d747be9df136b6f3729390298494947268743193c53d73d3c2e05df5d723cf2b779a4bc7bbd4fa1082ab1a37397e0d8c408d7b2546c69585ee158be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0a2646970667358221220569cf0d808cac332c4c262a1767de84e52faa5b8c88a16ef53b90f81f01ff5a864736f6c63430008140033000000000000000000000000398e92c827c5fa0f33f171dc8e20570c5cff330e000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf000000000000000000000000398e92c827c5fa0f33f171dc8e20570c5cff330e
Deployed Bytecode
0x6080604081815260048036101561001557600080fd5b600092833560e01c9081630f0cb0b9146114245750806318160ddd14610f865780632cbb43fc146114065780632d984723146113e8578063375c22cd146113a45780633f4ba83a14611335578063441a3e701461128d578063506384171461121b5780635b6c508c146110ba5780635c975abb14611093578063600381f0146110755780636071105814610ff3578063715018a614610fa55780637d88209714610f865780637f8661a114610e6257806381a4fdd814610e1a5780638456cb5914610db957806387cef13014610aae5780638980f11f146109bb5780638da5cb5b146109925780639f973fd5146108ab578063b7ec8d4b1461054e578063d2d48ee3146103f0578063d6d681771461035f578063de1b905214610343578063deac03011461031c578063e7d20283146102f2578063e91f323514610253578063ea32f42e146101e75763f2fde38b1461016d57600080fd5b346101e35760203660031901126101e35761018661147c565b9061018f6114c9565b6001600160a01b039182169283156101cd575050600180546001600160a01b03198116841790915516600080516020611d668339815191528380a380f35b51631e4fbdf760e01b8152908101849052602490fd5b8280fd5b83823461024f57602036600319011261024f5761020261147c565b61020a6114c9565b81546001600160a01b0319166001600160a01b03919091169081179091557fc35d72a054acf727dbcb414ab4b300b86c00481ccf567ab027bd57a0fb7727828280a280f35b5080fd5b50346101e35760603660031901126101e35761026d61147c565b90604435906001600160401b03908183116102ee57366023840112156102ee578201359081116102ea57369101602401116101e357816020936102e292856102b361190a565b01516001600160a01b03909116825285522080546001600160801b0381169160809190911c9060243590611b4c565b915191825250f35b8480fd5b8580fd5b50503461024f578060031936011261024f576102e28160209361031361147c565b856102b361190a565b50346101e357826003193601126101e3575490516001600160a01b03909116815260209150f35b50503461024f578160031936011261024f576020905160148152f35b5091903461024f578060031936011261024f5761037a61147c565b6001600160a01b031682526002602052808220805460243591908210156103ec57906103a591611497565b50908154600160ff82169301549282519460038210156103d95750845260081c63ffffffff16602084015282015260609150f35b634e487b7160e01b815260218752602490fd5b8380fd5b50346101e3576020806003193601126103ec57926001600160a01b0361041461147c565b168152600284528281209181908354835b81811061050e575061044e610439846115be565b936104468851958661159b565b8085526115be565b8388019590601f19013687378490855b8381106104a557505050505083519485948186019282875251809352850193925b82811061048e57505050500390f35b83518552869550938101939281019260010161047f565b60016104b782849c99989a9b9c611497565b5001546104d3575b6104c8906114f5565b98979694959861045e565b9187518110156104fb576104f38184896104c89460051b8c0101526114f5565b9290506104bf565b634e487b7160e01b865260328552602486fd5b600161051e82889a979698611497565b500154610539575b61052f906114f5565b9694929396610425565b9461054661052f916114f5565b959050610526565b5091903461024f57606036600319011261024f5760249283356001600160a01b038181169383359290918582036108a7576044359460038610156108a35761059461179e565b61059c61177d565b84156108955786156108775733885260209260028452848920549760148910156108675789918289610839575062278d00945b6001600160601b0390818a11610829576105ef6105ea6118cc565b611c8e565b8d3390528852888d2090815485828216958615610810575b505050831680940361080057908c969594939291818b16815460a01c019182116107ee5761067593929161063a916117e6565b61066f8a61064661190a565b936106518486611afd565b908c51838152600080516020611d268339815191528d3392a361151a565b916119c7565b3384526002855263ffffffff61068e878620944261151a565b169086519061069c82611534565b6107dc5788815285810191825286810188815284549094600160401b8210156107ca57906106cf91600182018155611497565b9590956107b95750519160038310156107a7575092600161079d969593600080516020611d468339815191529a9b9c936107919664ffffffff0060ff86549316918260ff1985161787555160081b169164ffffffffff1916171783555191015561073b8660035461151a565b6003558351916323b872dd60e01b9083015233908201523060448201528460648201526064815261076b81611580565b7f000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf611647565b519283923396846115d5565b0390a26001815580f35b634e487b7160e01b8b52602190528a8afd5b634e487b7160e01b81528084528c90fd5b634e487b7160e01b8752604185528d87fd5b634e487b7160e01b8552602183528b85fd5b634e487b7160e01b8852601186528e88fd5b885163be6422b760e01b81528690fd5b6001600160a01b03199092161783559350388581610607565b885163034c196960e31b81528690fd5b8b935060018a0361084f5750624f1a00946105cf565b948b93506001198a016105cf5762c5c10095506105cf565b85516308f0bb4160e21b81528390fd5b8351635a8af66b60e01b81529081906108919082016117c1565b0390fd5b8351633e9842ab60e01b8152fd5b8780fd5b8680fd5b5090346101e35760203660031901126101e3576108c661147c565b6001600160a01b0381811693909290841561097857506108e76105ea6118cc565b33865260205280852080546001600160a01b03198116861790915560a081901c93169161096c9061091661190a565b9061093461092d876109288886611afd565b611527565b86846119c7565b61093d86611809565b948451958652600080516020611d26833981519152958660203392a361066f866109678385611afd565b61151a565b5191825260203392a380f35b9051635a8af66b60e01b81529081906108919082016117c1565b50503461024f578160031936011261024f5760015490516001600160a01b039091168152602090f35b5082903461024f578260031936011261024f576109d661147c565b602435916109e26114c9565b6001600160a01b0382811691907f000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf168214610aa0578115610a74578315610a6657507f8c1256b8896378cd5044f80c202f9772b9d77dc85c8a6eb51967210b09bfaa289394610a5684610a6093339061160b565b51928392836115f0565b0390a180f35b8551633e9842ab60e01b8152fd5b6064906020875191635a8af66b60e01b835282015260056024820152643a37b5b2b760d91b6044820152fd5b85516305c6159560e31b8152fd5b5082903461024f57608036600319011261024f57610aca61147c565b602493843591604435916001600160a01b03808416908185036108a357606435936003851015610db557818854163303610da557610b0661179e565b610b0e61177d565b8615610d95578215610d7d5781169687895260209260028452848a2054966014881015610867578a918288610d4f575062278d00945b6001600160601b0390818c1161082957888e8e610b626105ea6118cc565b9082528a5220908154818116958615610d36575b5050841680930361080057908d969594939291818d16815460a01c01918211610d24578c8a8f94610bae610bdc979561066f956117e6565b610bb661190a565b95600080516020611d268339815191528d610bd1888a611afd565b9451868152a361151a565b8984526002855263ffffffff610bf5878620944261151a565b1690865190610c0382611534565b610d12578781528581019182528681018a815284549094600160401b821015610d005790610c3691600182018155611497565b959095610cef575051916003831015610cdd575092600161079d97969593600080516020611d468339815191529b9c9d93610cd39664ffffffff0060ff86549316918260ff1985161787555160081b169164ffffffffff19161717835551910155610ca38860035461151a565b6003558351916323b872dd60e01b9083015233908201523060448201528660648201526064815261076b81611580565b51938493846115d5565b634e487b7160e01b8c52602190528b8bfd5b634e487b7160e01b81528084528d90fd5b634e487b7160e01b8752604185528e87fd5b634e487b7160e01b8552602183528c85fd5b634e487b7160e01b8852601186528f88fd5b6001600160a01b03199091168517835594503880610b76565b8c935060018903610d655750624f1a0094610b44565b948c93506001198901610b445762c5c1009550610b44565b8351635a8af66b60e01b815280610891818b016117c1565b8351633e9842ab60e01b81528890fd5b83516303b22c3f60e51b81528890fd5b8880fd5b50503461024f578160031936011261024f5760207f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25891610df76114c9565b610dff61177d565b6001805460ff60a01b1916600160a01b17905551338152a180f35b50503461024f578060031936011261024f57602091600190610e58906001600160a01b03610e4661147c565b16815260028552836024359120611497565b5001549051908152f35b5091903461024f57602036600319011261024f57823590610e8161177d565b610e8961179e565b6000193384526002602052610ea083838620611497565b5090600182019182548015610f765763ffffffff825460081c164210610f6757808311610f5e575b5060ff9192610ed7338561182a565b610ee2848254611527565b9055610ef083600354611527565b600355610f1e83337f000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf61160b565b5416916003831015610f4b579061079d600080516020611d068339815191529392519283923396846115d5565b634e487b7160e01b855260218652602485fd5b915060ff610ec8565b845162b4ffe560e41b81528890fd5b845163460e84d760e11b81528890fd5b50503461024f578160031936011261024f576020906003549051908152f35b8334610ff05780600319360112610ff057610fbe6114c9565b600180546001600160a01b0319811690915581906001600160a01b0316600080516020611d668339815191528280a380f35b80fd5b5091903461024f578060031936011261024f576001600160a01b0361101661147c565b168252600260205261102c602435828420611497565b509081549160ff8316936003851015611062575060609450600163ffffffff91015492825194855260081c166020840152820152f35b634e487b7160e01b815260218652602490fd5b50503461024f578160031936011261024f5760209051624f1a008152f35b50503461024f578160031936011261024f5760209060ff60015460a01c1690519015158152f35b5090346101e357826003193601126101e357906110d561177d565b6110dd61179e565b338352600260205281832080548493849392845b8381106111615787868880611150575b5080611110575b506001815580f35b8061112061114a92600354611527565b600355337f000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf61160b565b81611108565b61115b90339061182a565b82611101565b61116b8183611497565b5096600188019081549182158015611208575b6111fa578261119f8160ff946111a5946111988380611527565b905561151a565b9961151a565b98541660038110156111e75790600080516020611d068339815191526111d76111df94938751918291339587846115d5565b0390a26114f5565b9594956110f1565b634e487b7160e01b8a526021875260248afd5b50969750506111df906114f5565b5063ffffffff8a5460081c16421061117e565b50503461024f57602036600319011261024f5761123661147c565b6001600160a01b03168252600260205280822080549092919082905b808210611263576020848451908152f35b90926112816112879160016112788789611497565b5001549061151a565b936114f5565b90611252565b5091903461024f578060031936011261024f576024359083356112ae61177d565b6112b661179e565b8081156113255733855260026020526112d184848720611497565b50600181019283549081156113155763ffffffff835460081c164210611306578110610f5e575060ff9192610ed7338561182a565b855162b4ffe560e41b81528990fd5b855163460e84d760e11b81528990fd5b8251633e9842ab60e01b81528690fd5b50346101e357826003193601126101e35761134e6114c9565b6001549060ff8260a01c1615611396575060ff60a01b1916600155513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa90602090a180f35b8251638dfc202b60e01b8152fd5b50503461024f578160031936011261024f57517f000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf6001600160a01b03168152602090f35b50503461024f578160031936011261024f576020905162278d008152f35b50503461024f578160031936011261024f576020905162c5c1008152f35b905083833461024f57602036600319011261024f576001600160a01b0361144961147c565b168252600260205281205460001981019190821161146957602083838152f35b634e487b7160e01b815260118452602490fd5b600435906001600160a01b038216820361149257565b600080fd5b80548210156114b35760005260206000209060011b0190600090565b634e487b7160e01b600052603260045260246000fd5b6001546001600160a01b031633036114dd57565b60405163118cdaa760e01b8152336004820152602490fd5b60001981146115045760010190565b634e487b7160e01b600052601160045260246000fd5b9190820180921161150457565b9190820391821161150457565b606081019081106001600160401b0382111761154f57604052565b634e487b7160e01b600052604160045260246000fd5b604081019081106001600160401b0382111761154f57604052565b60a081019081106001600160401b0382111761154f57604052565b601f909101601f19168101906001600160401b0382119082101761154f57604052565b6001600160401b03811161154f5760051b60200190565b91604091949360ff9160608501968552602085015216910152565b6001600160a01b039091168152602081019190915260400190565b611640611645939261163260405194859263a9059cbb60e01b6020850152602484016115f0565b03601f19810184528361159b565b611647565b565b60018060a01b031690600080826020829451910182865af13d1561170e573d906001600160401b0382116116fa57906116a291604051916116926020601f19601f840116018461159b565b82523d84602084013e5b8461171a565b9081519182151592836116d2575b5050506116ba5750565b60249060405190635274afe760e01b82526004820152fd5b81929350906020918101031261024f576020015190811591821503610ff057503880806116b0565b634e487b7160e01b83526041600452602483fd5b6116a29060609061169c565b90611741575080511561172f57805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580611774575b611752575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b1561174a565b60ff60015460a01c1661178c57565b60405163d93c066560e01b8152600490fd5b6002600054146117af576002600055565b604051633ee5aeb560e01b8152600490fd5b60609060208152600a6020820152693232b632b3b0ba34b7b760b11b60408201520190565b80546001600160a01b031660a09290921b6001600160a01b031916919091179055565b60008190039190600160ff1b81146001166115045760001983050361150457565b906001600160601b03908183116118ba576118466105ea6118cc565b9160018060a01b038092169283600052602052604060002090808516825460a01c0390811161150457600080516020611d26833981519152928261188f6118b1936020956117e6565b5416946118ac61189d61190a565b8761066f846109288385611afd565b611809565b604051908152a3565b60405163034c196960e31b8152600490fd5b604051906118d982611565565b60088252676465706f7369747360c01b6020830152565b604051906118fd82611565565b6000602083606081520152565b6119126118f0565b50604080519061192182611565565b600b825260206a3b37ba34b733a837bbb2b960a91b818401526119426118f0565b508151818101907f7b1a68ec3e3284b167e69db1c622dcfa612281976b71d7e2d239dbe16a75891a8252845184828560005b8481106119ad575050611996928101600083820152038581018452018261159b565b5190209151926119a584611565565b835282015290565b819350809250890101518782860101520182858792611974565b9091906001600160c01b038211611ad2576020908101516001600160a01b039093166000908152929052604082208054909290916001600160801b0391828416918183611aa1575b839043149182611a79575b4360c01b179087016001015515611a33575b5050505050565b60018201809211611a6557818460801c1015610ff057506001600160801b031990921691161790553880808080611a2c565b634e487b7160e01b81526011600452602490fd5b90506000198401848111611a8d5790611a1a565b634e487b7160e01b84526011600452602484fd5b506000198301838111611abe57611ab89087611c75565b50611a0f565b634e487b7160e01b83526011600452602483fd5b60405162461bcd60e51b815260206004820152600360248201526227b7a160e91b6044820152606490fd5b6020908101516001600160a01b0390921660009081529190526040902080546001600160801b03168015611b4557600019810190811161150457611b4091611c75565b905090565b5050600090565b919093928015611c405780821015611492576000199080820190811161150457919291905b818403611bc657505090611b8491611c75565b9210611b905760009190565b60405162461bcd60e51b815260206004820152600e60248201526d536561726368204661696c75726560901b6044820152606490fd5b611bd28285979561151a565b6001808201809211611c2b571c611be98185611c75565b9790868103611bff575050505050505060009190565b8698509590919293949510600014611c1d5750915b90929192611b71565b925081810190811115611c14575b60246000634e487b7160e01b81526011600452fd5b60405162461bcd60e51b815260206004820152600d60248201526c1d5b9a5b9a5d1a585b1a5e9959609a1b6044820152606490fd5b016001015460c081901c916001600160c01b0390911690565b604090815190602092838301937f03a912cdb153207069d92d44a2357e3f0ce00f7ee84da3510f1c6851b4cac4ee85528383516000945b818610611ced5750611ce794508101600084820152039081018452018261159b565b51902090565b85810184015187870186015294830194869250611cc556fe939e45c2a0bfec7a2bdfa3cb3e08e1bc7f8ba9dc5fe33aac70c75c949ebbb62433161cf2da28d747be9df136b6f3729390298494947268743193c53d73d3c2e05df5d723cf2b779a4bc7bbd4fa1082ab1a37397e0d8c408d7b2546c69585ee158be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0a2646970667358221220569cf0d808cac332c4c262a1767de84e52faa5b8c88a16ef53b90f81f01ff5a864736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000398e92c827c5fa0f33f171dc8e20570c5cff330e000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf000000000000000000000000398e92c827c5fa0f33f171dc8e20570c5cff330e
-----Decoded View---------------
Arg [0] : _owner (address): 0x398e92C827C5FA0F33F171DC8E20570c5CfF330e
Arg [1] : _arcd (address): 0xe020B01B6fbD83066aa2e8ee0CCD1eB8d9Cc70bF
Arg [2] : _airdropDistribution (address): 0x398e92C827C5FA0F33F171DC8E20570c5CfF330e
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000398e92c827c5fa0f33f171dc8e20570c5cff330e
Arg [1] : 000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf
Arg [2] : 000000000000000000000000398e92c827c5fa0f33f171dc8e20570c5cff330e
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 100.00% | $0.01145 | 599,277.765 | $6,862.01 |
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.