More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 2,030 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Withdraw | 21225613 | 10 days ago | IN | 0 ETH | 0.00094792 | ||||
Withdraw | 21077093 | 30 days ago | IN | 0 ETH | 0.00150472 | ||||
Withdraw | 21065084 | 32 days ago | IN | 0 ETH | 0.00234161 | ||||
Add Nft And Dele... | 21065080 | 32 days ago | IN | 0 ETH | 0.00288456 | ||||
Withdraw | 20774264 | 73 days ago | IN | 0 ETH | 0.00052388 | ||||
Withdraw | 20774018 | 73 days ago | IN | 0 ETH | 0.00062616 | ||||
Withdraw | 20660529 | 88 days ago | IN | 0 ETH | 0.00005628 | ||||
Withdraw | 20637813 | 92 days ago | IN | 0 ETH | 0.00014641 | ||||
Withdraw | 20626247 | 93 days ago | IN | 0 ETH | 0.00018833 | ||||
Withdraw | 20585881 | 99 days ago | IN | 0 ETH | 0.00018515 | ||||
Withdraw | 20428796 | 121 days ago | IN | 0 ETH | 0.00154922 | ||||
Withdraw | 20416991 | 122 days ago | IN | 0 ETH | 0.00018546 | ||||
Withdraw | 20416962 | 122 days ago | IN | 0 ETH | 0.00021129 | ||||
Withdraw | 20342226 | 133 days ago | IN | 0 ETH | 0.00060788 | ||||
Withdraw | 20339912 | 133 days ago | IN | 0 ETH | 0.00063528 | ||||
Withdraw | 20323195 | 136 days ago | IN | 0 ETH | 0.00064517 | ||||
Withdraw | 20285365 | 141 days ago | IN | 0 ETH | 0.00075302 | ||||
Withdraw | 20272555 | 143 days ago | IN | 0 ETH | 0.00035356 | ||||
Withdraw | 20218122 | 150 days ago | IN | 0 ETH | 0.00059089 | ||||
Withdraw | 20217448 | 150 days ago | IN | 0 ETH | 0.00038746 | ||||
Withdraw | 20124150 | 163 days ago | IN | 0 ETH | 0.00032151 | ||||
Withdraw | 20105027 | 166 days ago | IN | 0 ETH | 0.00051176 | ||||
Withdraw | 20105003 | 166 days ago | IN | 0 ETH | 0.00052271 | ||||
Withdraw | 20104987 | 166 days ago | IN | 0 ETH | 0.00055385 | ||||
Withdraw | 20058359 | 173 days ago | IN | 0 ETH | 0.00064105 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
NFTBoostVault
Compiler Version
v0.8.18+commit.87f61d96
Optimization Enabled:
Yes with 999999 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.18; import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "./libraries/BoundedHistory.sol"; import "./external/council/libraries/Storage.sol"; import "./libraries/NFTBoostVaultStorage.sol"; import "./interfaces/INFTBoostVault.sol"; import "./BaseVotingVault.sol"; import { NBV_HasRegistration, NBV_AlreadyDelegated, NBV_InsufficientBalance, NBV_InsufficientWithdrawableBalance, NBV_MultiplierLimit, NBV_NoMultiplierSet, NBV_InvalidNft, NBV_ZeroAmount, NBV_ZeroAddress, NBV_ArrayTooManyElements, NBV_Locked, NBV_AlreadyUnlocked, NBV_NotAirdrop, NBV_NoRegistration, NBV_WrongDelegatee, NBV_InvalidExpiration, NBV_MultiplierSet } from "./errors/Governance.sol"; /** * @title NFTBoostVault * @author Non-Fungible Technologies, Inc. * * The voting power for participants in this vault holding reputation ERC1155 nfts * is enhanced by a multiplier. This contract enables holders of specific ERC1155 nfts * to gain an advantage wrt voting power for participation in governance. Participants * send their ERC20 tokens to the contract and provide their ERC1155 nfts as calldata. * Once the contract confirms their ownership of the ERC1155 token id, and matches the * ERC1155 address and tokenId to a multiplier, they are able to delegate their voting * power for participation in governance. * * @dev There is no emergency withdrawal in this contract, any funds not sent via * addNftAndDelegate() are unrecoverable by this version of the NFTBoostVault. */ contract NFTBoostVault is INFTBoostVault, BaseVotingVault { using SafeERC20 for IERC20; // ======================================== STATE ================================================== // Bring History library into scope using BoundedHistory for BoundedHistory.HistoricalBalances; // ======================================== STATE ================================================== /// @dev Determines the maximum multiplier for any given NFT. /* solhint-disable var-name-mixedcase */ uint128 public constant MAX_MULTIPLIER = 1.5e3; /// @dev Precision of the multiplier. uint128 public constant MULTIPLIER_DENOMINATOR = 1e3; // ========================================== CONSTRUCTOR =========================================== /** * @notice Deploys a voting vault, setting immutable values for the token * and staleBlockLag. * * @param token The external erc20 token contract. * @param staleBlockLag The number of blocks before which the delegation history is forgotten. * @param timelock The address of the timelock who can update the manager address. * @param manager The address of the manager who can update the multiplier values. */ constructor( IERC20 token, uint256 staleBlockLag, address timelock, address manager ) BaseVotingVault(token, staleBlockLag) { if (timelock == address(0)) revert NBV_ZeroAddress("timelock"); if (manager == address(0)) revert NBV_ZeroAddress("manager"); Storage.set(Storage.uint256Ptr("initialized"), 1); Storage.set(Storage.addressPtr("timelock"), timelock); Storage.set(Storage.addressPtr("manager"), manager); Storage.set(Storage.uint256Ptr("entered"), 1); Storage.set(Storage.uint256Ptr("locked"), 1); } // ===================================== USER FUNCTIONALITY ========================================= /** * @notice Performs token and optional ERC1155 registration for the caller. The caller cannot have * an existing registration. * * @dev User has to own ERC1155 nft for receiving the benefits of a multiplier. * * @param amount Amount of tokens sent to this contract by the user for locking * in governance. * @param tokenId The id of the ERC1155 NFT. * @param tokenAddress The address of the ERC1155 token the user is registering for multiplier * access. * @param delegatee Optional param. The address to delegate the voting power associated * with this registration. */ function addNftAndDelegate( uint128 amount, uint128 tokenId, address tokenAddress, address delegatee ) external override nonReentrant { if (amount == 0) revert NBV_ZeroAmount(); _registerAndDelegate(msg.sender, amount, tokenId, tokenAddress, delegatee); // transfer user ERC20 amount and ERC1155 nft into this contract _lockTokens(msg.sender, uint256(amount), tokenAddress, tokenId); } /** * @notice Function for an airdrop contract to call to register a user or update * their registration with more tokens. * * @dev This function is only callable by the airdrop contract. * @dev If a user already has a registration, they cannot change their delegatee. * * @param user The address of the user to register. * @param amount Amount of token to transfer to this contract. * @param delegatee The address to delegate the voting power to. */ function airdropReceive( address user, uint128 amount, address delegatee ) external override onlyAirdrop nonReentrant { if (amount == 0) revert NBV_ZeroAmount(); if (user == address(0)) revert NBV_ZeroAddress("user"); // load the registration NFTBoostVaultStorage.Registration storage registration = _getRegistrations()[user]; // if user is not already registered, register them // else just update their registration if (registration.delegatee == address(0)) { _registerAndDelegate(user, amount, 0, address(0), delegatee); } else { // if user supplies new delegatee address revert if (delegatee != registration.delegatee) revert NBV_WrongDelegatee(delegatee, registration.delegatee); // get this contract's balance Storage.Uint256 storage balance = _balance(); // update contract balance balance.data += amount; // update registration amount registration.amount += amount; // sync current delegatee's voting power _syncVotingPower(user, registration); } // transfer user ERC20 amount only into this contract _lockTokens(msg.sender, uint256(amount), address(0), 0); } /** * @notice Changes the caller's token voting power delegation. * * @dev The total voting power is not guaranteed to go up because the token * multiplier can be updated at any time. * * @param to The address to delegate to. */ function delegate(address to) external override { if (to == address(0)) revert NBV_ZeroAddress("to"); NFTBoostVaultStorage.Registration storage registration = _getRegistrations()[msg.sender]; // user must have an existing registration if (registration.delegatee == address(0)) revert NBV_NoRegistration(); // If to address is already the delegate, don't send the tx if (to == registration.delegatee) revert NBV_AlreadyDelegated(); BoundedHistory.HistoricalBalances memory votingPower = _votingPower(); uint256 oldDelegateeVotes = votingPower.loadTop(registration.delegatee); // Remove voting power from old delegatee and emit event votingPower.push( registration.delegatee, oldDelegateeVotes - registration.latestVotingPower, MAX_HISTORY_LENGTH ); emit VoteChange(msg.sender, registration.delegatee, -1 * int256(uint256(registration.latestVotingPower))); // Note - It is important that this is loaded here and not before the previous state change because if // to == registration.delegatee and re-delegation was allowed we could be working with out of date state uint256 newDelegateeVotes = votingPower.loadTop(to); // return the current voting power of the Registration. Varies based on the multiplier associated with the // user's ERC1155 token at the time of txn uint256 addedVotingPower = _currentVotingPower(registration); // add voting power to the target delegatee and emit event votingPower.push(to, newDelegateeVotes + addedVotingPower, MAX_HISTORY_LENGTH); // update registration properties registration.latestVotingPower = uint128(addedVotingPower); registration.delegatee = to; emit VoteChange(msg.sender, to, int256(addedVotingPower)); } /** * @notice Removes a user's locked ERC20 tokens from this contract and if no tokens are remaining, the * user's locked ERC1155 (if utilized) is also transferred back to them. Consequently, the user's * delegatee loses the voting power associated with the aforementioned tokens. * * @dev Withdraw is unlocked when the locked state variable is set to 2. * * @param amount The amount of token to withdraw. */ function withdraw(uint128 amount) external override nonReentrant { if (getIsLocked() == 1) revert NBV_Locked(); if (amount == 0) revert NBV_ZeroAmount(); // load the registration NFTBoostVaultStorage.Registration storage registration = _getRegistrations()[msg.sender]; // get this contract's balance Storage.Uint256 storage balance = _balance(); if (balance.data < amount) revert NBV_InsufficientBalance(); // get the withdrawable amount uint256 withdrawable = _getWithdrawableAmount(registration); if (withdrawable < amount) revert NBV_InsufficientWithdrawableBalance(withdrawable); // update contract balance balance.data -= amount; // update withdrawn amount registration.withdrawn += amount; // update the delegatee's voting power. Varies based on the multiplier associated with the // user's ERC1155 token at the time of the call _syncVotingPower(msg.sender, registration); if (registration.withdrawn == registration.amount) { if (registration.tokenAddress != address(0) && registration.tokenId != 0) { _withdrawNft(); } // delete registration. tokenId and token address already set to 0 in _withdrawNft() registration.amount = 0; registration.latestVotingPower = 0; registration.withdrawn = 0; registration.delegatee = address(0); } // transfer the token amount to the user token.safeTransfer(msg.sender, amount); } /** * @notice Adds tokens to a user's registration. The user must have an existing registration. * * @param amount The amount of tokens to add. */ function addTokens(uint128 amount) external override nonReentrant { if (amount == 0) revert NBV_ZeroAmount(); // load the registration NFTBoostVaultStorage.Registration storage registration = _getRegistrations()[msg.sender]; // If the registration does not have a delegatee, revert because the Registration // is not initialized if (registration.delegatee == address(0)) revert NBV_NoRegistration(); // get this contract's balance Storage.Uint256 storage balance = _balance(); // update contract balance balance.data += amount; // update registration amount registration.amount += amount; // update the delegatee's voting power _syncVotingPower(msg.sender, registration); // transfer ERC20 amount into this contract _lockTokens(msg.sender, amount, address(0), 0); } /** * @notice Nonreentrant function that calls a helper when users want to withdraw * the ERC1155 NFT they are using in their registration. */ function withdrawNft() external override nonReentrant { _withdrawNft(); } /** * @notice A function that allows a user's to change the ERC1155 nft they are using for * accessing a voting power multiplier. Or if the users does not have a NFT * registered, they can register one and their voting power will be updated. * The provided ERC1155 token must have an associated multiplier to register it. * * @param newTokenAddress Address of the new ERC1155 token the user wants to use. * @param newTokenId Id of the new ERC1155 token the user wants to use. */ function updateNft(uint128 newTokenId, address newTokenAddress) external override nonReentrant { if (newTokenAddress == address(0) || newTokenId == 0) revert NBV_InvalidNft(newTokenAddress, newTokenId); // check there is a multiplier associated with the new NFT if (getMultiplier(newTokenAddress, newTokenId) == 0) revert NBV_NoMultiplierSet(); NFTBoostVaultStorage.Registration storage registration = _getRegistrations()[msg.sender]; // If the registration does not have a delegatee, revert because the Registration // is not initialized if (registration.delegatee == address(0)) revert NBV_NoRegistration(); // if the user already has an ERC1155 registered, withdraw it if (registration.tokenAddress != address(0)) { // withdraw the current ERC1155 from the registration _withdrawNft(); } // set the new ERC1155 values in the registration and lock the new ERC1155 registration.tokenAddress = newTokenAddress; registration.tokenId = newTokenId; _lockNft(msg.sender, newTokenAddress, newTokenId); // update the delegatee's voting power based on new ERC1155 nft's multiplier _syncVotingPower(msg.sender, registration); } /** * @notice Update users' registration voting power. * * @dev Voting power is only updated for this block onward. See Council contract History.sol * for more on how voting power is tracked and queried. * Anybody can update up to 50 users' registration voting power. * * @param userAddresses Array of addresses whose registration voting power this * function updates. */ function updateVotingPower(address[] calldata userAddresses) public override { if (userAddresses.length > 50) revert NBV_ArrayTooManyElements(); for (uint256 i = 0; i < userAddresses.length; ++i) { NFTBoostVaultStorage.Registration storage registration = _getRegistrations()[userAddresses[i]]; _syncVotingPower(userAddresses[i], registration); } } // ===================================== ADMIN FUNCTIONALITY ======================================== /** * @notice An onlyManager function for setting the multiplier value associated with an ERC1155 * contract address. The provided multiplier value must be less than or equal to 1.5x * and greater than or equal to 1x. Every multiplier value has an associated expiration * timestamp. Once a multiplier expires, the multiplier for the ERC1155 returns 1x. * Once a multiplier is set, it cannot be modified. * * @param tokenAddress ERC1155 token address to set the multiplier for. * @param tokenId The token ID of the ERC1155 for which the multiplier is being set. * @param multiplierValue The multiplier value corresponding to the token address and ID. * @param expiration The timestamp at which the multiplier expires. */ function setMultiplier( address tokenAddress, uint128 tokenId, uint128 multiplierValue, uint128 expiration ) public override onlyManager { if (multiplierValue > MAX_MULTIPLIER) revert NBV_MultiplierLimit("high"); if (multiplierValue < 1e3) revert NBV_MultiplierLimit("low"); if (expiration <= block.timestamp) revert NBV_InvalidExpiration(); if (tokenAddress == address(0) || tokenId == 0) revert NBV_InvalidNft(tokenAddress, tokenId); NFTBoostVaultStorage.MultiplierData storage multiplierData = _getMultipliers()[tokenAddress][tokenId]; // cannot modify multiplier data if it is already set if (multiplierData.multiplier != 0) { revert NBV_MultiplierSet(multiplierData.multiplier, multiplierData.expiration); } // set multiplier data multiplierData.multiplier = multiplierValue; multiplierData.expiration = expiration; emit MultiplierSet(tokenAddress, tokenId, multiplierValue, expiration); } /** * @notice An Timelock only function for ERC20 allowing withdrawals. * * @dev Allows the timelock to unlock withdrawals. Cannot be reversed. */ function unlock() external override onlyTimelock { if (getIsLocked() != 1) revert NBV_AlreadyUnlocked(); Storage.set(Storage.uint256Ptr("locked"), 2); emit WithdrawalsUnlocked(); } /** * @notice Manager-only airdrop contract address update function. * * @dev Allows the manager to update the airdrop contract address. * * @param newAirdropContract The address of the new airdrop contract. */ function setAirdropContract(address newAirdropContract) external override onlyManager { Storage.set(Storage.addressPtr("airdrop"), newAirdropContract); emit AirdropContractUpdated(newAirdropContract); } // ======================================= VIEW FUNCTIONS =========================================== /** * @notice Returns whether tokens can be withdrawn from the vault. * * @return locked Whether withdrawals are locked. */ function getIsLocked() public view override returns (uint256) { return Storage.uint256Ptr("locked").data; } /** * @notice A function to access a NFT's voting power multiplier. If the user does not provide * a token address and ID, the function returns the default 1x multiplier. This implies * that a registration without a token address and ID have a default 1x multiplier. * * @param tokenAddress ERC1155 token address to lookup. * @param tokenId The token ID of the ERC1155 to lookup. * * @return The token multiplier. */ function getMultiplier(address tokenAddress, uint128 tokenId) public view override returns (uint128) { // if NFT is not registered, return 1x multiplier if (tokenAddress == address(0) && tokenId == 0) return 1e3; NFTBoostVaultStorage.MultiplierData storage multiplierData = _getMultipliers()[tokenAddress][tokenId]; // if multiplier is not set, return 0 if (multiplierData.expiration == 0) return 0; // if multiplier has expired, return 1x multiplier if (multiplierData.expiration <= block.timestamp) return 1e3; return multiplierData.multiplier; } /** * @notice A function to access the storage of the nft's multiplier expiration. * * @param tokenAddress The address of the token. * @param tokenId The token ID. * * @return The multiplier's expiration. */ function getMultiplierExpiration(address tokenAddress, uint128 tokenId) external view override returns (uint128) { NFTBoostVaultStorage.MultiplierData storage multiplierData = _getMultipliers()[tokenAddress][tokenId]; return multiplierData.expiration; } /** * @notice Getter for the registrations mapping. * * @param who The owner of the registration to query. * * @return registration Registration of the provided address. */ function getRegistration(address who) external view override returns (NFTBoostVaultStorage.Registration memory) { return _getRegistrations()[who]; } /** * @notice A function to access the stored airdrop contract address. * * @return address The address of the airdrop contract. */ function getAirdropContract() external view override returns (address) { return Storage.addressPtr("airdrop").data; } // =========================================== HELPERS ============================================== /** * @notice A helper function to register a user and delegate their voting power. This function is called * when a user does not have a Registration created yet. * * @param user The address of the user to register. * @param _amount Amount of tokens to be locked. * @param _tokenId The id of the ERC1155 NFT. * @param _tokenAddress The address of the ERC1155 token. * @param _delegatee The address to delegate the voting power associated * with this registration. */ function _registerAndDelegate( address user, uint128 _amount, uint128 _tokenId, address _tokenAddress, address _delegatee ) internal { // check there is a multiplier associated with the ERC1155 uint128 multiplier = getMultiplier(_tokenAddress, _tokenId); if (multiplier == 0) revert NBV_NoMultiplierSet(); // load this contract's balance storage Storage.Uint256 storage balance = _balance(); // load the registration NFTBoostVaultStorage.Registration storage registration = _getRegistrations()[user]; // If the delegate address is not address zero, revert because the Registration // is already initialized. Only one Registration per user if (registration.delegatee != address(0)) revert NBV_HasRegistration(); // load the delegate. Defaults to the registration owner _delegatee = _delegatee == address(0) ? user : _delegatee; // calculate the voting power provided by this registration uint128 newVotingPower = (_amount * multiplier) / MULTIPLIER_DENOMINATOR; // set the new registration registration.amount = _amount; registration.latestVotingPower = newVotingPower; registration.withdrawn = 0; registration.tokenId = _tokenId; registration.tokenAddress = _tokenAddress; registration.delegatee = _delegatee; // update this contract's balance balance.data += _amount; _grantVotingPower(_delegatee, newVotingPower); emit VoteChange(user, _delegatee, int256(uint256(newVotingPower))); } /** * @dev Grants the chosen delegate address voting power when a new user registers. * * @param delegatee The address to delegate the voting power associated * with the Registration to. * @param newVotingPower Amount of votingPower associated with this Registration to * be added to delegates existing votingPower. * */ function _grantVotingPower(address delegatee, uint128 newVotingPower) internal { // update the delegatee's voting power BoundedHistory.HistoricalBalances memory votingPower = _votingPower(); // loads the most recent timestamp of voting power for this delegate uint256 delegateeVotes = votingPower.loadTop(delegatee); // add block stamp indexed delegation power for this delegate to historical data array votingPower.push(delegatee, delegateeVotes + newVotingPower, MAX_HISTORY_LENGTH); } /** * @dev A single function endpoint for loading Registration storage * * @dev Only one Registration is allowed per user. * * @return registrations A storage mapping to look up registrations data */ function _getRegistrations() internal pure returns (mapping(address => NFTBoostVaultStorage.Registration) storage) { // This call returns a storage mapping with a unique non overwrite-able storage location. return NFTBoostVaultStorage.mappingAddressToRegistrationPtr("registrations"); } /** * @notice Helper function called when a user wants to withdraw the ERC1155 NFT * they have registered for accessing a voting power multiplier. */ function _withdrawNft() internal { // load the registration NFTBoostVaultStorage.Registration storage registration = _getRegistrations()[msg.sender]; if (registration.tokenAddress == address(0)) { revert NBV_InvalidNft(registration.tokenAddress, registration.tokenId); } // transfer ERC1155 back to the user IERC1155(registration.tokenAddress).safeTransferFrom( address(this), msg.sender, registration.tokenId, 1, bytes("") ); // remove ERC1155 values from registration struct registration.tokenAddress = address(0); registration.tokenId = 0; // update the delegatee's voting power based on multiplier removal _syncVotingPower(msg.sender, registration); } /** * @dev Helper to update a delegatee's voting power. * * @param who The address who's voting power we need to sync. * * @param registration The storage pointer to the registration of that user. */ function _syncVotingPower(address who, NFTBoostVaultStorage.Registration storage registration) internal { BoundedHistory.HistoricalBalances memory votingPower = _votingPower(); uint256 delegateeVotes = votingPower.loadTop(registration.delegatee); uint256 newVotingPower = _currentVotingPower(registration); // get the change in voting power. Negative if the voting power is reduced int256 change = int256(newVotingPower) - int256(uint256(registration.latestVotingPower)); // do nothing if there is no change if (change == 0) return; if (change > 0) { votingPower.push(registration.delegatee, delegateeVotes + uint256(change), MAX_HISTORY_LENGTH); } else if (delegateeVotes > uint256(change * -1)) { // if the change is negative, we multiply by -1 to avoid underflow when casting votingPower.push(registration.delegatee, delegateeVotes - uint256(change * -1), MAX_HISTORY_LENGTH); } else { votingPower.push(registration.delegatee, 0, MAX_HISTORY_LENGTH); } registration.latestVotingPower = uint128(newVotingPower); emit VoteChange(who, registration.delegatee, change); } /** * @dev Calculates how much a user can withdraw. * * @param registration The the memory location of the loaded registration. * * @return withdrawable Amount which can be withdrawn. */ function _getWithdrawableAmount( NFTBoostVaultStorage.Registration memory registration ) internal pure returns (uint256) { if (registration.withdrawn == registration.amount) { return 0; } return registration.amount - registration.withdrawn; } /** * @dev Helper that returns the current voting power of a registration. * * @dev This is not always the recorded voting power since it uses the latest multiplier. * * @param registration The registration to check for voting power. * * @return The current voting power of the registration. */ function _currentVotingPower( NFTBoostVaultStorage.Registration memory registration ) internal view virtual returns (uint256) { uint128 locked = registration.amount - registration.withdrawn; if (registration.tokenAddress != address(0) && registration.tokenId != 0) { return (locked * getMultiplier(registration.tokenAddress, registration.tokenId)) / MULTIPLIER_DENOMINATOR; } return locked; } /** * @notice An internal function for locking a user's ERC20 tokens in this contract * for participation in governance. Calls _lockNft function if an ERC1155 * token address and ID are specified. * * @param from Address tokens are transferred from. * @param amount Amount of ERC20 tokens being transferred. * @param tokenAddress Address of the ERC1155 token being transferred. * @param tokenId ID of the ERC1155 token being transferred. */ function _lockTokens(address from, uint256 amount, address tokenAddress, uint128 tokenId) internal { token.transferFrom(from, address(this), amount); if (tokenAddress != address(0) && tokenId != 0) { _lockNft(from, tokenAddress, tokenId); } } /** * @dev A internal function for locking a user's ERC1155 token in this contract * for participation in governance. * * @param from Address of owner token is transferred from. * @param tokenAddress Address of the token being transferred. * @param tokenId Id of the token being transferred. */ function _lockNft(address from, address tokenAddress, uint128 tokenId) internal { IERC1155(tokenAddress).safeTransferFrom(from, address(this), tokenId, 1, bytes("")); } /** @dev A single function endpoint for loading storage for multipliers. * * @return A storage mapping which can be used to lookup a * token's multiplier data and token id data. */ function _getMultipliers() internal pure returns (mapping(address => mapping(uint128 => NFTBoostVaultStorage.MultiplierData)) storage) { // This call returns a storage mapping with a unique non overwrite-able storage layout. return NFTBoostVaultStorage.mappingAddressToMultiplierData("multipliers"); } /** @dev A function to handles the receipt of a single ERC1155 token. This function is called * at the end of a safeTransferFrom after the balance has been updated. To accept the transfer, * this must return bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) * * @return 0xf23a6e61 */ function onERC1155Received(address, address, uint256, uint256, bytes memory) public virtual returns (bytes4) { return this.onERC1155Received.selector; } modifier onlyAirdrop() { if (msg.sender != Storage.addressPtr("airdrop").data) revert NBV_NotAirdrop(); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC1155 compliant contract, as defined in the * https://eips.ethereum.org/EIPS/eip-1155[EIP]. * * _Available since v3.1._ */ interface IERC1155 is IERC165 { /** * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`. */ event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); /** * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all * transfers. */ event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values ); /** * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to * `approved`. */ event ApprovalForAll(address indexed account, address indexed operator, bool approved); /** * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI. * * If an {URI} event was emitted for `id`, the standard * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value * returned by {IERC1155MetadataURI-uri}. */ event URI(string value, uint256 indexed id); /** * @dev Returns the amount of tokens of token type `id` owned by `account`. * * Requirements: * * - `account` cannot be the zero address. */ function balanceOf(address account, uint256 id) external view returns (uint256); /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}. * * Requirements: * * - `accounts` and `ids` must have the same length. */ function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory); /** * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`, * * Emits an {ApprovalForAll} event. * * Requirements: * * - `operator` cannot be the caller. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns true if `operator` is approved to transfer ``account``'s tokens. * * See {setApprovalForAll}. */ function isApprovedForAll(address account, address operator) external view returns (bool); /** * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}. * - `from` must have a balance of tokens of type `id` of at least `amount`. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes calldata data ) external; /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}. * * Emits a {TransferBatch} event. * * Requirements: * * - `ids` and `amounts` must have the same length. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function safeBatchTransferFrom( address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) 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 `amount` 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 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /** * @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); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../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; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @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, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; assembly { size := extcodesize(account) } return size > 0; } /** * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @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, it is bubbled up by this * function (like regular Solidity function calls). * * 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. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @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`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // 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 assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.18; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./libraries/BoundedHistory.sol"; import "./external/council/libraries/Storage.sol"; import "./libraries/HashedStorageReentrancyBlock.sol"; import "./interfaces/IBaseVotingVault.sol"; import { BVV_NotManager, BVV_NotTimelock, BVV_ZeroAddress, BVV_UpperLimitBlock } from "./errors/Governance.sol"; /** * @title BaseVotingVault * @author Non-Fungible Technologies, Inc. * * This contract is a base voting vault contract for use with Arcade voting vaults. * It includes basic voting vault functions like querying vote power, setting * the timelock and manager addresses, and getting the contracts token balance. */ abstract contract BaseVotingVault is HashedStorageReentrancyBlock, IBaseVotingVault { // ======================================== STATE ================================================== // Bring libraries into scope using BoundedHistory for BoundedHistory.HistoricalBalances; // ============================================ STATE =============================================== /// @notice The token used for voting in this vault. IERC20 public immutable token; /// @notice Number of blocks after which history can be pruned. uint256 public immutable staleBlockLag; /// @dev Max length of any voting history. Prevents gas exhaustion /// attacks from having too-large history. uint256 public constant MAX_HISTORY_LENGTH = 256; // ============================================ EVENTS ============================================== // Event to track delegation data event VoteChange(address indexed from, address indexed to, int256 amount); // ========================================== CONSTRUCTOR =========================================== /** * @notice Deploys a base voting vault, setting immutable values for the token * and staleBlockLag. * * @param _token The external erc20 token contract. * @param _staleBlockLag The number of blocks before which the delegation history is forgotten. */ constructor(IERC20 _token, uint256 _staleBlockLag) { if (address(_token) == address(0)) revert BVV_ZeroAddress("token"); if (_staleBlockLag >= block.number) revert BVV_UpperLimitBlock(_staleBlockLag); token = _token; staleBlockLag = _staleBlockLag; } // ==================================== TIMELOCK FUNCTIONALITY ====================================== /** * @notice Timelock-only timelock update function. * @dev Allows the timelock to update the timelock address. * * @param timelock_ The new timelock. */ function setTimelock(address timelock_) external onlyTimelock { if (timelock_ == address(0)) revert BVV_ZeroAddress("timelock"); Storage.set(Storage.addressPtr("timelock"), timelock_); } /** * @notice Timelock-only manager update function. * @dev Allows the timelock to update the manager address. * * @param manager_ The new manager address. */ function setManager(address manager_) external onlyTimelock { if (manager_ == address(0)) revert BVV_ZeroAddress("manager"); Storage.set(Storage.addressPtr("manager"), manager_); } // ======================================= VIEW FUNCTIONS =========================================== /** * @notice Loads the voting power of a user. * * @param user The address we want to load the voting power of. * @param blockNumber Block number to query the user's voting power at. * * @return votes The number of votes. */ function queryVotePower(address user, uint256 blockNumber, bytes calldata) external override returns (uint256) { // Get our reference to historical data BoundedHistory.HistoricalBalances memory votingPower = _votingPower(); // Find the historical data and clear everything more than 'staleBlockLag' into the past return votingPower.findAndClear(user, blockNumber, block.number - staleBlockLag); } /** * @notice Loads the voting power of a user without changing state. * * @param user The address we want to load the voting power of. * @param blockNumber Block number to query the user's voting power at. * * @return votes The number of votes. */ function queryVotePowerView(address user, uint256 blockNumber) external view returns (uint256) { // Get our reference to historical data BoundedHistory.HistoricalBalances memory votingPower = _votingPower(); // Find the historical datum return votingPower.find(user, blockNumber); } /** * @notice A function to access the storage of the timelock address. * @dev The timelock can access all functions with the onlyTimelock modifier. * * @return timelock The timelock address. */ function timelock() public view returns (address) { return _timelock().data; } /** * @notice A function to access the storage of the manager address. * * @dev The manager can access all functions with the onlyManager modifier. * * @return manager The manager address. */ function manager() public view returns (address) { return _manager().data; } // =========================================== HELPERS ============================================== /** * @notice A function to access the storage of the token value * * @return balance A struct containing the balance uint. */ function _balance() internal pure returns (Storage.Uint256 storage) { return Storage.uint256Ptr("balance"); } /** * @notice A function to access the storage of the timelock address. * * @dev The timelock can access all functions with the onlyTimelock modifier. * * @return timelock A struct containing the timelock address. */ function _timelock() internal view returns (Storage.Address storage) { return Storage.addressPtr("timelock"); } /** * @notice A function to access the storage of the manager address. * * @dev The manager can access all functions with the onlyManager modifier. * * @return manager A struct containing the manager address. */ function _manager() internal view returns (Storage.Address storage) { return Storage.addressPtr("manager"); } /** * @notice Returns the historical voting power tracker. * * @return votingPower Historical voting power tracker. */ function _votingPower() internal pure returns (BoundedHistory.HistoricalBalances memory) { // This call returns a storage mapping with a unique non overwrite-able storage location. return BoundedHistory.load("votingPower"); } /** * @notice Modifier to check that the caller is the manager. */ modifier onlyManager() { if (msg.sender != manager()) revert BVV_NotManager(); _; } /** * @notice Modifier to check that the caller is the timelock. */ modifier onlyTimelock() { if (msg.sender != timelock()) revert BVV_NotTimelock(); _; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.18; /** * @title GovernanceErrors * @author Non-Fungible Technologies, Inc. * * This file contains custom errors for the Arcade governance vault contracts. All errors * are prefixed by the contract that throws them (e.g., "NBV_" for NFTBoostVault). * Errors located in one place to make it possible to holistically look at all * governance failure cases. */ // ======================================== NFT BOOST VAULT ========================================== /// @notice All errors prefixed with NBV_, to separate from other contracts in governance. /** * @notice Ensure caller has not already registered. */ error NBV_HasRegistration(); /** * @notice Caller has not already registered. */ error NBV_NoRegistration(); /** * @notice Ensure delegatee is not already registered as the delegate in user's Registration. */ error NBV_AlreadyDelegated(); /** * @notice Contract balance has to be bigger than amount being withdrawn. */ error NBV_InsufficientBalance(); /** * @notice Withdrawable tokens less than withdraw request amount. * * @param withdrawable The returned withdrawable amount from * a user's registration. */ error NBV_InsufficientWithdrawableBalance(uint256 withdrawable); /** * @notice Multiplier limit exceeded. * * @param limitType Whether the multiplier is too high or too low. */ error NBV_MultiplierLimit(string limitType); /** * @notice No multiplier has been set for the specified ERC1155 token. */ error NBV_NoMultiplierSet(); /** * @notice Multiplier has already been set for the specified ERC1155 token. */ error NBV_MultiplierSet(uint128 multiplier, uint128 expiration); /** * @notice The provided token address and token id are invalid. * * @param tokenAddress The token address provided. * @param tokenId The token id provided. */ error NBV_InvalidNft(address tokenAddress, uint256 tokenId); /** * @notice User is calling withdraw() with zero amount. */ error NBV_ZeroAmount(); /** * @notice Zero address passed in where not allowed. * * @param addressType The name of the parameter for which * a zero address was provided. */ error NBV_ZeroAddress(string addressType); /** * @notice Provided addresses array holds more than 50 addresses. */ error NBV_ArrayTooManyElements(); /** @notice NFT Boost Voting Vault has already been unlocked. */ error NBV_AlreadyUnlocked(); /** * @notice ERC20 withdrawals from NFT Boost Voting Vault are frozen. */ error NBV_Locked(); /** * @notice Airdrop contract is not the caller. */ error NBV_NotAirdrop(); /** * @notice If a user already has a registration, they cannot change their * delegatee when claiming subsequent airdrops. */ error NBV_WrongDelegatee(address newDelegate, address currentDelegate); /** * @notice The multiplier expiration provided has already passed. */ error NBV_InvalidExpiration(); // ==================================== VESTING VOTING VAULT ====================================== /// @notice All errors prefixed with AVV_, to separate from other contracts in governance. /** * @notice Block number parameters used to create a grant are invalid. Check that the start time is * before the cliff, and the cliff is before the expiration. */ error AVV_InvalidSchedule(); /** * @notice The cliff block number cannot be less than the current block. */ error AVV_InvalidCliff(); /** * @notice Cliff amount should be less than the grant amount. */ error AVV_InvalidCliffAmount(); /** * @notice Insufficient balance to carry out the transaction. * * @param amountAvailable The amount available in the vault. */ error AVV_InsufficientBalance(uint256 amountAvailable); /** * @notice Grant has already been created for specified user. */ error AVV_HasGrant(); /** * @notice Grant has not been created for the specified user. */ error AVV_NoGrantSet(); /** * @notice Tokens cannot be claimed before the cliff. * * @param cliffBlock The block number when grant claims begin. */ error AVV_CliffNotReached(uint256 cliffBlock); /** * @notice Tokens cannot be re-delegated to the same address. */ error AVV_AlreadyDelegated(); /** * @notice Cannot withdraw zero tokens. */ error AVV_InvalidAmount(); /** * @notice Zero address passed in where not allowed. * * @param addressType The name of the parameter for which * a zero address was provided. */ error AVV_ZeroAddress(string addressType); // =================================== IMMUTABLE VESTING VAULT =================================== /// @notice All errors prefixed with IVV_, to separate from other contracts in governance. /** * @notice Grants cannot be revoked from the immutable vesting vault. */ error IVV_ImmutableGrants(); // ====================================== BASE VOTING VAULT ====================================== /// @notice All errors prefixed with BVV_, to separate from other contracts in governance. /** * @notice Caller is not the manager. */ error BVV_NotManager(); /** * @notice Caller is not the timelock. */ error BVV_NotTimelock(); /** * @notice Zero address passed in where not allowed. * * @param addressType The name of the parameter for which a zero * address was provided. */ error BVV_ZeroAddress(string addressType); /** * @notice The provided stale block number is too high. * * @param staleBlock The block number in the past, provided at deployment * before which a user's history is pruned. */ error BVV_UpperLimitBlock(uint256 staleBlock);
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.3; 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, // block number of the data loaded & 0x0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff // the data ); } /// @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.3; // 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 pragma solidity 0.8.18; interface IBaseVotingVault { function queryVotePower(address user, uint256 blockNumber, bytes calldata extraData) external returns (uint256); function queryVotePowerView(address user, uint256 blockNumber) external view returns (uint256); function setTimelock(address timelock_) external; function setManager(address manager_) external; function timelock() external view returns (address); function manager() external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.18; import "../libraries/NFTBoostVaultStorage.sol"; interface INFTBoostVault { /** * @notice Events */ event MultiplierSet(address tokenAddress, uint128 tokenId, uint128 multiplier, uint128 expiration); event WithdrawalsUnlocked(); event AirdropContractUpdated(address newAirdropContract); /** * @notice View functions */ function getIsLocked() external view returns (uint256); function getRegistration(address who) external view returns (NFTBoostVaultStorage.Registration memory); function getMultiplier(address tokenAddress, uint128 tokenId) external view returns (uint128); function getMultiplierExpiration(address tokenAddress, uint128 tokenId) external view returns (uint128); function getAirdropContract() external view returns (address); /** * @notice NFT boost vault functionality */ function addNftAndDelegate(uint128 amount, uint128 tokenId, address tokenAddress, address delegatee) external; function airdropReceive(address user, uint128 amount, address delegatee) external; function delegate(address to) external; function withdraw(uint128 amount) external; function addTokens(uint128 amount) external; function withdrawNft() external; function updateNft(uint128 newTokenId, address newTokenAddress) external; function updateVotingPower(address[] memory userAddresses) external; /** * @notice Only Manager function */ function setMultiplier(address tokenAddress, uint128 tokenId, uint128 multiplierValue, uint128 expiration) external; /** * @notice Only Timelock function */ function unlock() external; /** * @notice Only Airdrop contract function */ function setAirdropContract(address _newAirdropContract) external; }
// SPDX-License-Identifier: MIT /* solhint-disable max-line-length */ /* solhint-disable reason-string */ pragma solidity 0.8.18; import "../external/council/libraries/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 BoundedHistory { // 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 /// @param maxLength The maximum length of history array, if at max, the oldest entry is removed function push(HistoricalBalances memory wrapper, address who, uint256 data, uint256 maxLength) 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 (loadedBlockNumber == block.number) { // 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. index = length - 1; } else if (length - minIndex >= maxLength) { // We need to push to the array, but if array is full to maxLength, so // we clear the oldest entry and increment the minIndex _clear(minIndex, ++minIndex, storageData); } // 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, // block number of the data loaded & 0x0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff // the data ); } /// @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: MIT pragma solidity 0.8.18; import "../external/council/libraries/History.sol"; import "../external/council/libraries/Storage.sol"; /** * @title HashedStorageReentrancyBlock * @author Non-Fungible Technologies, Inc. * * Helper contract to prevent reentrancy attacks using hashed storage. This contract is used * to protect against reentrancy attacks in the Arcade voting vault contracts. */ abstract contract HashedStorageReentrancyBlock { // =========================================== STATE ================================================ // ============== CONSTANTS ============== uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; // =========================================== HELPERS ============================================== /** * @dev Returns the storage pointer to the entered state variable. * * @return Storage pointer to the entered state variable. */ function _entered() internal pure returns (Storage.Uint256 storage) { return Storage.uint256Ptr("entered"); } // ========================================= MODIFIERS ============================================= /** * @dev Re-entrancy guard modifier using hashed storage. */ modifier nonReentrant() { Storage.Uint256 storage entered = _entered(); // Check the state variable before the call is entered require(entered.data == _NOT_ENTERED, "REENTRANCY"); // Store that the function has been entered Storage.set(entered, _ENTERED); // Run the function code _; // Clear the state Storage.set(entered, _NOT_ENTERED); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.18; /** * @title NFTBoostVaultStorage * @author Non-Fungible Technologies, Inc. * * Contract based on Council's `Storage.sol` with modified scope to match the NFTBoostVault * requirements. This library will return storage pointers based on a hashed name and type string. */ library NFTBoostVaultStorage { /** * 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 typehash of the 'MultiplierData' mapping bytes32 public constant MULTIPLIER_TYPEHASH = keccak256("mapping(address => mapping(uint128 => MultiplierData))"); /// @dev typehash of the 'Registration' mapping bytes32 public constant REGISTRATION_TYPEHASH = keccak256("mapping(address => Registration)"); /// @dev struct which represents 1 packed storage location (Registration) struct Registration { uint128 amount; // token amount uint128 latestVotingPower; uint128 withdrawn; // amount of tokens withdrawn from voting vault uint128 tokenId; // ERC1155 token id address tokenAddress; // the address of the ERC1155 token address delegatee; } /// @dev struct which represents 1 packed storage location (MultiplierData) struct MultiplierData { uint128 multiplier; uint128 expiration; } /** * @notice Returns the storage pointer for a mapping of address to registration data * * @param name The variable name for the pointer. * * @return data The mapping pointer. */ function mappingAddressToRegistrationPtr( string memory name ) internal pure returns (mapping(address => Registration) storage data) { bytes32 offset = keccak256(abi.encodePacked(REGISTRATION_TYPEHASH, name)); assembly { data.slot := offset } } /** * @notice Returns the storage pointer for a mapping of address to a uint128 pair * * @param name The variable name for the pointer. * * @return data The mapping pointer. */ function mappingAddressToMultiplierData( string memory name ) internal pure returns (mapping(address => mapping(uint128 => MultiplierData)) storage data) { bytes32 offset = keccak256(abi.encodePacked(MULTIPLIER_TYPEHASH, name)); assembly { data.slot := offset } } }
{ "metadata": { "bytecodeHash": "none" }, "optimizer": { "enabled": true, "runs": 999999 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"staleBlockLag","type":"uint256"},{"internalType":"address","name":"timelock","type":"address"},{"internalType":"address","name":"manager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BVV_NotManager","type":"error"},{"inputs":[],"name":"BVV_NotTimelock","type":"error"},{"inputs":[{"internalType":"uint256","name":"staleBlock","type":"uint256"}],"name":"BVV_UpperLimitBlock","type":"error"},{"inputs":[{"internalType":"string","name":"addressType","type":"string"}],"name":"BVV_ZeroAddress","type":"error"},{"inputs":[],"name":"NBV_AlreadyDelegated","type":"error"},{"inputs":[],"name":"NBV_AlreadyUnlocked","type":"error"},{"inputs":[],"name":"NBV_ArrayTooManyElements","type":"error"},{"inputs":[],"name":"NBV_HasRegistration","type":"error"},{"inputs":[],"name":"NBV_InsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"withdrawable","type":"uint256"}],"name":"NBV_InsufficientWithdrawableBalance","type":"error"},{"inputs":[],"name":"NBV_InvalidExpiration","type":"error"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NBV_InvalidNft","type":"error"},{"inputs":[],"name":"NBV_Locked","type":"error"},{"inputs":[{"internalType":"string","name":"limitType","type":"string"}],"name":"NBV_MultiplierLimit","type":"error"},{"inputs":[{"internalType":"uint128","name":"multiplier","type":"uint128"},{"internalType":"uint128","name":"expiration","type":"uint128"}],"name":"NBV_MultiplierSet","type":"error"},{"inputs":[],"name":"NBV_NoMultiplierSet","type":"error"},{"inputs":[],"name":"NBV_NoRegistration","type":"error"},{"inputs":[],"name":"NBV_NotAirdrop","type":"error"},{"inputs":[{"internalType":"address","name":"newDelegate","type":"address"},{"internalType":"address","name":"currentDelegate","type":"address"}],"name":"NBV_WrongDelegatee","type":"error"},{"inputs":[{"internalType":"string","name":"addressType","type":"string"}],"name":"NBV_ZeroAddress","type":"error"},{"inputs":[],"name":"NBV_ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newAirdropContract","type":"address"}],"name":"AirdropContractUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint128","name":"tokenId","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"multiplier","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"expiration","type":"uint128"}],"name":"MultiplierSet","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":[],"name":"WithdrawalsUnlocked","type":"event"},{"inputs":[],"name":"MAX_HISTORY_LENGTH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_MULTIPLIER","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MULTIPLIER_DENOMINATOR","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint128","name":"tokenId","type":"uint128"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"address","name":"delegatee","type":"address"}],"name":"addNftAndDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"amount","type":"uint128"}],"name":"addTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"address","name":"delegatee","type":"address"}],"name":"airdropReceive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAirdropContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getIsLocked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint128","name":"tokenId","type":"uint128"}],"name":"getMultiplier","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint128","name":"tokenId","type":"uint128"}],"name":"getMultiplierExpiration","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"}],"name":"getRegistration","outputs":[{"components":[{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint128","name":"latestVotingPower","type":"uint128"},{"internalType":"uint128","name":"withdrawn","type":"uint128"},{"internalType":"uint128","name":"tokenId","type":"uint128"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"address","name":"delegatee","type":"address"}],"internalType":"struct NFTBoostVaultStorage.Registration","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"manager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","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":"nonpayable","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":"newAirdropContract","type":"address"}],"name":"setAirdropContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"manager_","type":"address"}],"name":"setManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint128","name":"tokenId","type":"uint128"},{"internalType":"uint128","name":"multiplierValue","type":"uint128"},{"internalType":"uint128","name":"expiration","type":"uint128"}],"name":"setMultiplier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"timelock_","type":"address"}],"name":"setTimelock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"staleBlockLag","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"timelock","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"newTokenId","type":"uint128"},{"internalType":"address","name":"newTokenAddress","type":"address"}],"name":"updateNft","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"userAddresses","type":"address[]"}],"name":"updateVotingPower","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"amount","type":"uint128"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawNft","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60c06040523480156200001157600080fd5b506040516200451538038062004515833981016040819052620000349162000366565b83836001600160a01b0382166200007b5760405163026cd36960e11b81526020600482015260056024820152643a37b5b2b760d91b60448201526064015b60405180910390fd5b438110620000a0576040516310fad7af60e01b81526004810182905260240162000072565b6001600160a01b0391821660805260a0528216620000ed57604051632c537de760e21b815260206004820152600860248201526774696d656c6f636b60c01b604482015260640162000072565b6001600160a01b0381166200013057604051632c537de760e21b815260206004820152600760248201526636b0b730b3b2b960c91b604482015260640162000072565b620001806200016d6040518060400160405280600b81526020016a1a5b9a5d1a585b1a5e995960aa1b8152506200029260201b6200233e1760201c565b6001620002ef60201b620023b71760201c565b620001cc620001ba6040518060400160405280600881526020016774696d656c6f636b60c01b815250620002f360201b620023bb1760201c565b836200033060201b620023f61760201c565b62000217620002056040518060400160405280600781526020016636b0b730b3b2b960c91b815250620002f360201b620023bb1760201c565b826200033060201b620023f61760201c565b620002506200016d60405180604001604052806007815260200166195b9d195c995960ca1b8152506200029260201b6200233e1760201c565b620002886200016d604051806040016040528060068152602001651b1bd8dad95960d21b8152506200029260201b6200233e1760201c565b50505050620003fc565b6000807fec13d6d12b88433319b64e1065a96ea19cd330ef6603f5f6fb685dde3959a320905060008184604051602001620002cf929190620003c2565b60408051601f198184030181529190528051602090910120949350505050565b9055565b6000807f421683f821a0574472445355be6d2b769119e8515f8376a1d7878523dfdecf7b905060008184604051602001620002cf929190620003c2565b81546001600160a01b0319166001600160a01b0391909116179055565b6001600160a01b03811681146200036357600080fd5b50565b600080600080608085870312156200037d57600080fd5b84516200038a816200034d565b602086015160408701519195509350620003a4816200034d565b6060860151909250620003b7816200034d565b939692955090935050565b8281526000825160005b81811015620003ea57602081860181015185830182015201620003cc565b50600092016020019182525092915050565b60805160a0516140de62000437600039600081816103b601526122100152600081816104ad015281816108c60152612b0f01526140de6000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c80638ed013a6116100f9578063d33219b411610097578063f23a6e6111610071578063f23a6e611461042c578063fa2435fd14610495578063fc0c546a146104a8578063fe6ca782146104cf57600080fd5b8063d33219b4146103fe578063e7d2028314610406578063e91f32351461041957600080fd5b8063bdacb303116100d3578063bdacb3031461039e578063c2c94b88146103b1578063c428ee9d146103d8578063d0ebdbe7146103eb57600080fd5b80638ed013a61461037b578063a69df4b514610383578063b8a807c11461038b57600080fd5b80635c19a95c116101665780636f011538116101405780636f011538146102b757806372731062146102ca57806381360e7a1461035f5780638c55ea581461036857600080fd5b80635c19a95c146102885780635d6a618d1461029b5780635f041903146102a457600080fd5b8063481c6a75116101a2578063481c6a751461022a578063561c9f4e1461025757806357b9ca361461026a578063583b18961461027257600080fd5b806302387a7b146101c95780632dec063f146101de57806340d5b0d914610217575b600080fd5b6101dc6101d736600461397e565b6104d8565b005b6101f16101ec3660046139bd565b61090a565b6040516fffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6101dc6102253660046139f0565b610a1a565b610232610db5565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161020e565b6101dc61026536600461397e565b610ddb565b610232610fd6565b61027a611016565b60405190815260200161020e565b6101dc610296366004613a44565b61105c565b6101f16105dc81565b6101dc6102b2366004613a5f565b61144e565b6101dc6102c5366004613a44565b6116e8565b6102dd6102d8366004613a44565b611821565b60405161020e9190600060c0820190506fffffffffffffffffffffffffffffffff80845116835280602085015116602084015280604085015116604084015280606085015116606084015250608083015173ffffffffffffffffffffffffffffffffffffffff80821660808501528060a08601511660a0850152505092915050565b61027a61010081565b6101dc610376366004613a89565b6118fa565b6101dc611c66565b6101dc611cf3565b6101dc610399366004613acc565b611e12565b6101dc6103ac366004613a44565b611f11565b61027a7f000000000000000000000000000000000000000000000000000000000000000081565b6101f16103e63660046139bd565b61203b565b6101dc6103f9366004613a44565b6120a5565b6102326121cf565b61027a610414366004613b15565b6121d9565b61027a610427366004613b3f565b6121f9565b61046461043a366004613bf5565b7ff23a6e610000000000000000000000000000000000000000000000000000000095945050505050565b6040517fffffffff00000000000000000000000000000000000000000000000000000000909116815260200161020e565b6101dc6104a3366004613cf9565b612248565b6102327f000000000000000000000000000000000000000000000000000000000000000081565b6101f16103e881565b60006104e2612438565b8054909150600114610555576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b60028155610561611016565b60010361059a576040517f94ceaa1f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b816fffffffffffffffffffffffffffffffff166000036105e6576040517fe11543e100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006105f061247d565b33600090815260209190915260408120915061060a6124bd565b9050836fffffffffffffffffffffffffffffffff168160000154101561065c576040517f59cb57c000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160c08101825283546fffffffffffffffffffffffffffffffff8082168352700100000000000000000000000000000000918290048116602084015260018601548082169484019490945292049091166060820152600283015473ffffffffffffffffffffffffffffffffffffffff908116608083015260038401541660a08201526000906106ed906124fd565b9050846fffffffffffffffffffffffffffffffff1681101561073e576040517f5d9272870000000000000000000000000000000000000000000000000000000081526004810182905260240161054c565b846fffffffffffffffffffffffffffffffff168260000160008282546107649190613d9d565b90915550506001830180548691906000906107929084906fffffffffffffffffffffffffffffffff16613db0565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055506107d23384612563565b825460018401546fffffffffffffffffffffffffffffffff9182169116036108ac57600283015473ffffffffffffffffffffffffffffffffffffffff16158015906108465750600183015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1615155b15610853576108536127de565b600083556001830180547fffffffffffffffffffffffffffffffff000000000000000000000000000000001690556003830180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b6108ff73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016336fffffffffffffffffffffffffffffffff88166129b0565b505060018255505050565b600073ffffffffffffffffffffffffffffffffffffffff831615801561094057506fffffffffffffffffffffffffffffffff8216155b1561094e57506103e8610a14565b6000610958612a3d565b73ffffffffffffffffffffffffffffffffffffffff85166000908152602091825260408082206fffffffffffffffffffffffffffffffff8088168452935281208054909350700100000000000000000000000000000000900490911690036109c4576000915050610a14565b8054427001000000000000000000000000000000009091046fffffffffffffffffffffffffffffffff16116109fe576103e8915050610a14565b546fffffffffffffffffffffffffffffffff1690505b92915050565b610a22610db5565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610a86576040517fe927ce0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6105dc6fffffffffffffffffffffffffffffffff83161115610b06576040517fd0f03f9d00000000000000000000000000000000000000000000000000000000815260040161054c9060208082526004908201527f6869676800000000000000000000000000000000000000000000000000000000604082015260600190565b6103e8826fffffffffffffffffffffffffffffffff161015610b84576040517fd0f03f9d00000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f6c6f770000000000000000000000000000000000000000000000000000000000604482015260640161054c565b42816fffffffffffffffffffffffffffffffff1611610bcf576040517f1a37ebf400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84161580610c0257506fffffffffffffffffffffffffffffffff8316155b15610c69576040517f3205f1c600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851660048201526fffffffffffffffffffffffffffffffff8416602482015260440161054c565b6000610c73612a3d565b73ffffffffffffffffffffffffffffffffffffffff86166000908152602091825260408082206fffffffffffffffffffffffffffffffff80891684529352902080549092501615610d245780546040517ffda8d4740000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8083166004830152700100000000000000000000000000000000909204909116602482015260440161054c565b6fffffffffffffffffffffffffffffffff828116700100000000000000000000000000000000810285831690811784556040805173ffffffffffffffffffffffffffffffffffffffff8a168152938816602085015283015260608201527fb6c4db7a42dfab7553d15168f88363d3af3f85739e3ec0d1f30c96028fe32f8f9060800160405180910390a15050505050565b6000610dbf612a7d565b5473ffffffffffffffffffffffffffffffffffffffff16919050565b6000610de5612438565b8054909150600114610e53576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e435900000000000000000000000000000000000000000000604482015260640161054c565b60028155816fffffffffffffffffffffffffffffffff16600003610ea3576040517fe11543e100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610ead61247d565b33600090815260209190915260409020600381015490915073ffffffffffffffffffffffffffffffffffffffff16610f11576040517ffa267cd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610f1b6124bd565b9050836fffffffffffffffffffffffffffffffff16816000016000828254610f439190613de0565b9091555050815484908390600090610f6e9084906fffffffffffffffffffffffffffffffff16613db0565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550610fae3383612563565b610fcd33856fffffffffffffffffffffffffffffffff16600080612abd565b50506001905550565b6000610dbf6040518060400160405280600781526020017f61697264726f70000000000000000000000000000000000000000000000000008152506123bb565b60006110566040518060400160405280600681526020017f6c6f636b6564000000000000000000000000000000000000000000000000000081525061233e565b54919050565b73ffffffffffffffffffffffffffffffffffffffff81166110d9576040517fb14df79c00000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f746f000000000000000000000000000000000000000000000000000000000000604482015260640161054c565b60006110e361247d565b33600090815260209190915260409020600381015490915073ffffffffffffffffffffffffffffffffffffffff16611147576040517ffa267cd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600381015473ffffffffffffffffffffffffffffffffffffffff9081169083160361119e576040517fa7b752f200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006111a8612bc3565b60038301549091506000906111d490839073ffffffffffffffffffffffffffffffffffffffff16612c16565b600384015484549192506112369173ffffffffffffffffffffffffffffffffffffffff9091169061122b9070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1684613d9d565b849190610100612cba565b6003830154835473ffffffffffffffffffffffffffffffffffffffff9091169033907f33161cf2da28d747be9df136b6f3729390298494947268743193c53d73d3c2e0906112ca9070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613df3565b60405190815260200160405180910390a360006112e78386612c16565b6040805160c08101825286546fffffffffffffffffffffffffffffffff8082168352700100000000000000000000000000000000918290048116602084015260018901548082169484019490945292049091166060820152600286015473ffffffffffffffffffffffffffffffffffffffff908116608083015260038701541660a082015290915060009061137b90612e23565b90506113968661138b8385613de0565b869190610100612cba565b84546fffffffffffffffffffffffffffffffff80831670010000000000000000000000000000000002911617855560038501805473ffffffffffffffffffffffffffffffffffffffff88167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116811790915560405133907f33161cf2da28d747be9df136b6f3729390298494947268743193c53d73d3c2e09061143e9085815260200190565b60405180910390a3505050505050565b6000611458612438565b80549091506001146114c6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e435900000000000000000000000000000000000000000000604482015260640161054c565b6002815573ffffffffffffffffffffffffffffffffffffffff821615806114fd57506fffffffffffffffffffffffffffffffff8316155b15611564576040517f3205f1c600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201526fffffffffffffffffffffffffffffffff8416602482015260440161054c565b61156e828461090a565b6fffffffffffffffffffffffffffffffff166000036115b9576040517fc985ca0800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006115c361247d565b33600090815260209190915260409020600381015490915073ffffffffffffffffffffffffffffffffffffffff16611627576040517ffa267cd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600281015473ffffffffffffffffffffffffffffffffffffffff161561164f5761164f6127de565b60028101805473ffffffffffffffffffffffffffffffffffffffff85167fffffffffffffffffffffffff00000000000000000000000000000000000000009091161790556001810180546fffffffffffffffffffffffffffffffff8087167001000000000000000000000000000000000291161790556116d0338486612ec2565b6116da3382612563565b50600190555050565b505050565b6116f0610db5565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611754576040517fe927ce0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6117d56117956040518060400160405280600781526020017f61697264726f70000000000000000000000000000000000000000000000000008152506123bb565b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8416179055565b60405173ffffffffffffffffffffffffffffffffffffffff821681527fabe40da5dd07060349f485f71bd70d796ba49859153e786fe58a1d11f86c4ac99060200160405180910390a150565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915261185b61247d565b73ffffffffffffffffffffffffffffffffffffffff92831660009081526020918252604090819020815160c08101835281546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000091829004811695830195909552600183015480861694830194909452909204909216606082015260028201548416608082015260039091015490921660a08301525090565b6119386040518060400160405280600781526020017f61697264726f70000000000000000000000000000000000000000000000000008152506123bb565b5473ffffffffffffffffffffffffffffffffffffffff163314611987576040517fab04925b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611991612438565b80549091506001146119ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e435900000000000000000000000000000000000000000000604482015260640161054c565b60028155826fffffffffffffffffffffffffffffffff16600003611a4f576040517fe11543e100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8416611ace576040517fb14df79c00000000000000000000000000000000000000000000000000000000815260040161054c9060208082526004908201527f7573657200000000000000000000000000000000000000000000000000000000604082015260600190565b6000611ad861247d565b73ffffffffffffffffffffffffffffffffffffffff80871660009081526020929092526040909120600381015490925016611b2057611b1b858560008087612f60565b611c3b565b600381015473ffffffffffffffffffffffffffffffffffffffff848116911614611b9c5760038101546040517fa044d4d100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8086166004830152909116602482015260440161054c565b6000611ba66124bd565b9050846fffffffffffffffffffffffffffffffff16816000016000828254611bce9190613de0565b9091555050815485908390600090611bf99084906fffffffffffffffffffffffffffffffff16613db0565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550611c398683612563565b505b611c5a33856fffffffffffffffffffffffffffffffff16600080612abd565b50600181555b50505050565b6000611c70612438565b8054909150600114611cde576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e435900000000000000000000000000000000000000000000604482015260640161054c565b60028155611cea6127de565b60019055565b50565b611cfb6121cf565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611d5f576040517f88fe7b4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d67611016565b600114611da0576040517fedc4227400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611de7611de16040518060400160405280600681526020017f6c6f636b6564000000000000000000000000000000000000000000000000000081525061233e565b60029055565b6040517f15fa93f6df3cbaf0b9ddb555fa798ba412e2bbc86e60aa62d8aade4c3052cd6290600090a1565b6000611e1c612438565b8054909150600114611e8a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e435900000000000000000000000000000000000000000000604482015260640161054c565b60028155846fffffffffffffffffffffffffffffffff16600003611eda576040517fe11543e100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ee73386868686612f60565b611f0533866fffffffffffffffffffffffffffffffff168587612abd565b600181555b5050505050565b611f196121cf565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611f7d576040517f88fe7b4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116611ffa576040517f04d9a6d200000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f74696d656c6f636b000000000000000000000000000000000000000000000000604482015260640161054c565b611cf06117956040518060400160405280600881526020017f74696d656c6f636b0000000000000000000000000000000000000000000000008152506123bb565b600080612046612a3d565b73ffffffffffffffffffffffffffffffffffffffff85166000908152602091825260408082206fffffffffffffffffffffffffffffffff8088168452935290205470010000000000000000000000000000000090041691505092915050565b6120ad6121cf565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612111576040517f88fe7b4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811661218e576040517f04d9a6d200000000000000000000000000000000000000000000000000000000815260206004820152600760248201527f6d616e6167657200000000000000000000000000000000000000000000000000604482015260640161054c565b611cf06117956040518060400160405280600781526020017f6d616e61676572000000000000000000000000000000000000000000000000008152506123bb565b6000610dbf6131b4565b6000806121e4612bc3565b90506121f18185856131f4565b949350505050565b600080612204612bc3565b905061223e86866122357f000000000000000000000000000000000000000000000000000000000000000043613d9d565b84929190613265565b9695505050505050565b6032811115612283576040517f48a3bbbc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b818110156116e357600061229861247d565b60008585858181106122ac576122ac613e3f565b90506020020160208101906122c19190613a44565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905061232d84848481811061231257612312613e3f565b90506020020160208101906123279190613a44565b82612563565b5061233781613e6e565b9050612286565b6000807fec13d6d12b88433319b64e1065a96ea19cd330ef6603f5f6fb685dde3959a320905060008184604051602001612379929190613eca565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190528051602090910120949350505050565b9055565b6000807f421683f821a0574472445355be6d2b769119e8515f8376a1d7878523dfdecf7b905060008184604051602001612379929190613eca565b81547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff91909116179055565b60006124786040518060400160405280600781526020017f656e74657265640000000000000000000000000000000000000000000000000081525061233e565b905090565b60006124786040518060400160405280600d81526020017f726567697374726174696f6e73000000000000000000000000000000000000008152506132f7565b60006124786040518060400160405280600781526020017f62616c616e63650000000000000000000000000000000000000000000000000081525061233e565b600081600001516fffffffffffffffffffffffffffffffff1682604001516fffffffffffffffffffffffffffffffff160361253a57506000919050565b6040820151825161254b9190613ef0565b6fffffffffffffffffffffffffffffffff1692915050565b600061256d612bc3565b600383015490915060009061259990839073ffffffffffffffffffffffffffffffffffffffff16612c16565b6040805160c08101825285546fffffffffffffffffffffffffffffffff8082168352700100000000000000000000000000000000918290048116602084015260018801548082169484019490945292049091166060820152600285015473ffffffffffffffffffffffffffffffffffffffff908116608083015260038601541660a082015290915060009061262d90612e23565b84549091506000906126659070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1683613f19565b90508060000361267757505050505050565b60008113156126ae5760038501546126a99073ffffffffffffffffffffffffffffffffffffffff1661138b8386613de0565b61275d565b6126d8817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613df3565b8311156127325760038501546126a99073ffffffffffffffffffffffffffffffffffffffff16612728837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613df3565b61138b9086613d9d565b600385015461275d90859073ffffffffffffffffffffffffffffffffffffffff166000610100612cba565b84546fffffffffffffffffffffffffffffffff808416700100000000000000000000000000000000029116178555600385015460405173ffffffffffffffffffffffffffffffffffffffff918216918816907f33161cf2da28d747be9df136b6f3729390298494947268743193c53d73d3c2e09061143e9085815260200190565b60006127e861247d565b33600090815260209190915260409020600281015490915073ffffffffffffffffffffffffffffffffffffffff1661289a57600281015460018201546040517f3205f1c600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909216600483015270010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16602482015260440161054c565b6002810154600180830154604080516020810182526000815290517ff242432a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9094169363f242432a9361293093309333937001000000000000000000000000000000009092046fffffffffffffffffffffffffffffffff169291600401613f83565b600060405180830381600087803b15801561294a57600080fd5b505af115801561295e573d6000803e3d6000fd5b5050506002820180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055506001810180546fffffffffffffffffffffffffffffffff169055611cf03382612563565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001790526116e390849061336a565b60006124786040518060400160405280600b81526020017f6d756c7469706c69657273000000000000000000000000000000000000000000815250613476565b60006124786040518060400160405280600781526020017f6d616e61676572000000000000000000000000000000000000000000000000008152506123bb565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8581166004830152306024830152604482018590527f000000000000000000000000000000000000000000000000000000000000000016906323b872dd906064016020604051808303816000875af1158015612b58573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b7c9190613fda565b5073ffffffffffffffffffffffffffffffffffffffff821615801590612bb357506fffffffffffffffffffffffffffffffff811615155b15611c6057611c60848383612ec2565b6040805180820190915260608152600060208201526124786040518060400160405280600b81526020017f766f74696e67506f7765720000000000000000000000000000000000000000008152506134ac565b600080612c24846020015190565b73ffffffffffffffffffffffffffffffffffffffff841660009081526020919091526040812080549092506fffffffffffffffffffffffffffffffff1690819003612c7457600092505050610a14565b6000612caf83612c85600185613d9d565b016001015460c081901c9177ffffffffffffffffffffffffffffffffffffffffffffffff90911690565b979650505050505050565b77ffffffffffffffffffffffffffffffffffffffffffffffff821115612d3c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4f6f420000000000000000000000000000000000000000000000000000000000604482015260640161054c565b6000612d49856020015190565b73ffffffffffffffffffffffffffffffffffffffff851660009081526020829052604081208054929350914360c01b9186831791608081901c916fffffffffffffffffffffffffffffffff909116908115612db157612dad86612c85600185613d9d565b5090505b81438203612dcb57612dc4600184613d9d565b9050612df1565b88612dd68585613d9d565b10612df157612df184612de881613e6e565b955085896134e5565b8481600189010155438214612e1557612e158785612e10866001613de0565b61350e565b505050505050505050505050565b60008082604001518360000151612e3a9190613ef0565b608084015190915073ffffffffffffffffffffffffffffffffffffffff1615801590612e7b575060608301516fffffffffffffffffffffffffffffffff1615155b1561254b576103e8612e958460800151856060015161090a565b612e9f9083613ffc565b612ea9919061405f565b6fffffffffffffffffffffffffffffffff169392505050565b604080516020810182526000815290517ff242432a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84169163f242432a91612f299187913091879160019190600401613f83565b600060405180830381600087803b158015612f4357600080fd5b505af1158015612f57573d6000803e3d6000fd5b50505050505050565b6000612f6c838561090a565b9050806fffffffffffffffffffffffffffffffff16600003612fba576040517fc985ca0800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612fc46124bd565b90506000612fd061247d565b73ffffffffffffffffffffffffffffffffffffffff808a166000908152602092909252604090912060038101549092501615613038576040517f9e2b1ab100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84161561305a578361305c565b875b935060006103e861306d858a613ffc565b613077919061405f565b6fffffffffffffffffffffffffffffffff808216700100000000000000000000000000000000908102828c169081178655918a1602600185015560028401805473ffffffffffffffffffffffffffffffffffffffff808b167fffffffffffffffffffffffff000000000000000000000000000000000000000092831617909255600386018054928a16929091169190911790558454919250908490600090613120908490613de0565b9091555061313090508582613537565b8473ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff167f33161cf2da28d747be9df136b6f3729390298494947268743193c53d73d3c2e0836fffffffffffffffffffffffffffffffff166040516131a191815260200190565b60405180910390a3505050505050505050565b60006124786040518060400160405280600881526020017f74696d656c6f636b0000000000000000000000000000000000000000000000008152506123bb565b600080613202856020015190565b73ffffffffffffffffffffffffffffffffffffffff85166000908152602082905260408120805492935091608081901c916fffffffffffffffffffffffffffffffff909116906132558488838686613571565b96505050505050505b9392505050565b600080613273866020015190565b73ffffffffffffffffffffffffffffffffffffffff86166000908152602082905260408120805492935091608081901c916fffffffffffffffffffffffffffffffff90911690806132c7858a8a8787613571565b91509150838211156132e9576132de8483876134e5565b6132e985838561350e565b9a9950505050505050505050565b6000807ff162ab2a93c7e839fe65139803001cff34be31efd14d829854af05b9911b6e848360405160200161332d929190613eca565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209392505050565b60006133cc826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff1661374c9092919063ffffffff16565b8051909150156116e357808060200190518101906133ea9190613fda565b6116e3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161054c565b6000807fafcb3a8867bd263fd503fc00b0ffd9a453f69f3268e9d48926d67e3802a891d98360405160200161332d929190613eca565b60408051808201909152606081526000602082015260006134cc8361375b565b6040805180820190915293845260208401525090919050565b818311156134f257600080fd5b60018101835b83811015611f0a576000828201556001016134f8565b80821061351a57600080fd5b6fffffffffffffffffffffffffffffffff1660809190911b179055565b6000613541612bc3565b9050600061354f8285612c16565b9050611c608461122b6fffffffffffffffffffffffffffffffff861684613de0565b600080826000036135de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f756e696e697469616c697a656400000000000000000000000000000000000000604482015260640161054c565b858511156135eb57600080fd5b8284106135f757600080fd5b6000613604600185613d9d565b90508460005b8282146136a7576000600261361f8585613de0565b61362a906001613de0565b613634919061408e565b6001818d01015490915060c081901c9077ffffffffffffffffffffffffffffffffffffffffffffffff168b8203613675579296509194506137429350505050565b8b821015613691578a821015613689578293505b82945061369f565b61369c600184613d9d565b95505b50505061360a565b60018a8301015460c081901c9077ffffffffffffffffffffffffffffffffffffffffffffffff168a821115613738576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f536561726368204661696c757265000000000000000000000000000000000000604482015260640161054c565b9195509093505050505b9550959350505050565b60606121f18484600085613796565b6000807f7b1a68ec3e3284b167e69db1c622dcfa612281976b71d7e2d239dbe16a75891a905060008184604051602001612379929190613eca565b606082471015613828576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161054c565b843b613890576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161054c565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516138b991906140a2565b60006040518083038185875af1925050503d80600081146138f6576040519150601f19603f3d011682016040523d82523d6000602084013e6138fb565b606091505b5091509150612caf8282866060831561391557508161325e565b8251156139255782518084602001fd5b816040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161054c91906140be565b80356fffffffffffffffffffffffffffffffff8116811461397957600080fd5b919050565b60006020828403121561399057600080fd5b61325e82613959565b803573ffffffffffffffffffffffffffffffffffffffff8116811461397957600080fd5b600080604083850312156139d057600080fd5b6139d983613999565b91506139e760208401613959565b90509250929050565b60008060008060808587031215613a0657600080fd5b613a0f85613999565b9350613a1d60208601613959565b9250613a2b60408601613959565b9150613a3960608601613959565b905092959194509250565b600060208284031215613a5657600080fd5b61325e82613999565b60008060408385031215613a7257600080fd5b613a7b83613959565b91506139e760208401613999565b600080600060608486031215613a9e57600080fd5b613aa784613999565b9250613ab560208501613959565b9150613ac360408501613999565b90509250925092565b60008060008060808587031215613ae257600080fd5b613aeb85613959565b9350613af960208601613959565b9250613b0760408601613999565b9150613a3960608601613999565b60008060408385031215613b2857600080fd5b613b3183613999565b946020939093013593505050565b60008060008060608587031215613b5557600080fd5b613b5e85613999565b935060208501359250604085013567ffffffffffffffff80821115613b8257600080fd5b818701915087601f830112613b9657600080fd5b813581811115613ba557600080fd5b886020828501011115613bb757600080fd5b95989497505060200194505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080600080600060a08688031215613c0d57600080fd5b613c1686613999565b9450613c2460208701613999565b93506040860135925060608601359150608086013567ffffffffffffffff80821115613c4f57600080fd5b818801915088601f830112613c6357600080fd5b813581811115613c7557613c75613bc6565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715613cbb57613cbb613bc6565b816040528281528b6020848701011115613cd457600080fd5b8260208601602083013760006020848301015280955050505050509295509295909350565b60008060208385031215613d0c57600080fd5b823567ffffffffffffffff80821115613d2457600080fd5b818501915085601f830112613d3857600080fd5b813581811115613d4757600080fd5b8660208260051b8501011115613d5c57600080fd5b60209290920196919550909350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610a1457610a14613d6e565b6fffffffffffffffffffffffffffffffff818116838216019080821115613dd957613dd9613d6e565b5092915050565b80820180821115610a1457610a14613d6e565b808202600082127f800000000000000000000000000000000000000000000000000000000000000084141615613e2b57613e2b613d6e565b8181058314821517610a1457610a14613d6e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613e9f57613e9f613d6e565b5060010190565b60005b83811015613ec1578181015183820152602001613ea9565b50506000910152565b82815260008251613ee2816020850160208701613ea6565b919091016020019392505050565b6fffffffffffffffffffffffffffffffff828116828216039080821115613dd957613dd9613d6e565b8181036000831280158383131683831282161715613dd957613dd9613d6e565b60008151808452613f51816020860160208601613ea6565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600073ffffffffffffffffffffffffffffffffffffffff80881683528087166020840152506fffffffffffffffffffffffffffffffff8516604083015283606083015260a06080830152612caf60a0830184613f39565b600060208284031215613fec57600080fd5b8151801515811461325e57600080fd5b6fffffffffffffffffffffffffffffffff81811683821602808216919082811461402857614028613d6e565b505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006fffffffffffffffffffffffffffffffff8084168061408257614082614030565b92169190910492915050565b60008261409d5761409d614030565b500490565b600082516140b4818460208701613ea6565b9190910192915050565b60208152600061325e6020830184613f3956fea164736f6c6343000812000a000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf0000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000002c845ac4bac48a6cd1e1c88a84195b7d5805b8200000000000000000000000002c845ac4bac48a6cd1e1c88a84195b7d5805b82
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101c45760003560e01c80638ed013a6116100f9578063d33219b411610097578063f23a6e6111610071578063f23a6e611461042c578063fa2435fd14610495578063fc0c546a146104a8578063fe6ca782146104cf57600080fd5b8063d33219b4146103fe578063e7d2028314610406578063e91f32351461041957600080fd5b8063bdacb303116100d3578063bdacb3031461039e578063c2c94b88146103b1578063c428ee9d146103d8578063d0ebdbe7146103eb57600080fd5b80638ed013a61461037b578063a69df4b514610383578063b8a807c11461038b57600080fd5b80635c19a95c116101665780636f011538116101405780636f011538146102b757806372731062146102ca57806381360e7a1461035f5780638c55ea581461036857600080fd5b80635c19a95c146102885780635d6a618d1461029b5780635f041903146102a457600080fd5b8063481c6a75116101a2578063481c6a751461022a578063561c9f4e1461025757806357b9ca361461026a578063583b18961461027257600080fd5b806302387a7b146101c95780632dec063f146101de57806340d5b0d914610217575b600080fd5b6101dc6101d736600461397e565b6104d8565b005b6101f16101ec3660046139bd565b61090a565b6040516fffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6101dc6102253660046139f0565b610a1a565b610232610db5565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161020e565b6101dc61026536600461397e565b610ddb565b610232610fd6565b61027a611016565b60405190815260200161020e565b6101dc610296366004613a44565b61105c565b6101f16105dc81565b6101dc6102b2366004613a5f565b61144e565b6101dc6102c5366004613a44565b6116e8565b6102dd6102d8366004613a44565b611821565b60405161020e9190600060c0820190506fffffffffffffffffffffffffffffffff80845116835280602085015116602084015280604085015116604084015280606085015116606084015250608083015173ffffffffffffffffffffffffffffffffffffffff80821660808501528060a08601511660a0850152505092915050565b61027a61010081565b6101dc610376366004613a89565b6118fa565b6101dc611c66565b6101dc611cf3565b6101dc610399366004613acc565b611e12565b6101dc6103ac366004613a44565b611f11565b61027a7f0000000000000000000000000000000000000000000000000000000000030d4081565b6101f16103e63660046139bd565b61203b565b6101dc6103f9366004613a44565b6120a5565b6102326121cf565b61027a610414366004613b15565b6121d9565b61027a610427366004613b3f565b6121f9565b61046461043a366004613bf5565b7ff23a6e610000000000000000000000000000000000000000000000000000000095945050505050565b6040517fffffffff00000000000000000000000000000000000000000000000000000000909116815260200161020e565b6101dc6104a3366004613cf9565b612248565b6102327f000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf81565b6101f16103e881565b60006104e2612438565b8054909150600114610555576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b60028155610561611016565b60010361059a576040517f94ceaa1f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b816fffffffffffffffffffffffffffffffff166000036105e6576040517fe11543e100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006105f061247d565b33600090815260209190915260408120915061060a6124bd565b9050836fffffffffffffffffffffffffffffffff168160000154101561065c576040517f59cb57c000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160c08101825283546fffffffffffffffffffffffffffffffff8082168352700100000000000000000000000000000000918290048116602084015260018601548082169484019490945292049091166060820152600283015473ffffffffffffffffffffffffffffffffffffffff908116608083015260038401541660a08201526000906106ed906124fd565b9050846fffffffffffffffffffffffffffffffff1681101561073e576040517f5d9272870000000000000000000000000000000000000000000000000000000081526004810182905260240161054c565b846fffffffffffffffffffffffffffffffff168260000160008282546107649190613d9d565b90915550506001830180548691906000906107929084906fffffffffffffffffffffffffffffffff16613db0565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055506107d23384612563565b825460018401546fffffffffffffffffffffffffffffffff9182169116036108ac57600283015473ffffffffffffffffffffffffffffffffffffffff16158015906108465750600183015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1615155b15610853576108536127de565b600083556001830180547fffffffffffffffffffffffffffffffff000000000000000000000000000000001690556003830180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b6108ff73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf16336fffffffffffffffffffffffffffffffff88166129b0565b505060018255505050565b600073ffffffffffffffffffffffffffffffffffffffff831615801561094057506fffffffffffffffffffffffffffffffff8216155b1561094e57506103e8610a14565b6000610958612a3d565b73ffffffffffffffffffffffffffffffffffffffff85166000908152602091825260408082206fffffffffffffffffffffffffffffffff8088168452935281208054909350700100000000000000000000000000000000900490911690036109c4576000915050610a14565b8054427001000000000000000000000000000000009091046fffffffffffffffffffffffffffffffff16116109fe576103e8915050610a14565b546fffffffffffffffffffffffffffffffff1690505b92915050565b610a22610db5565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610a86576040517fe927ce0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6105dc6fffffffffffffffffffffffffffffffff83161115610b06576040517fd0f03f9d00000000000000000000000000000000000000000000000000000000815260040161054c9060208082526004908201527f6869676800000000000000000000000000000000000000000000000000000000604082015260600190565b6103e8826fffffffffffffffffffffffffffffffff161015610b84576040517fd0f03f9d00000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f6c6f770000000000000000000000000000000000000000000000000000000000604482015260640161054c565b42816fffffffffffffffffffffffffffffffff1611610bcf576040517f1a37ebf400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84161580610c0257506fffffffffffffffffffffffffffffffff8316155b15610c69576040517f3205f1c600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851660048201526fffffffffffffffffffffffffffffffff8416602482015260440161054c565b6000610c73612a3d565b73ffffffffffffffffffffffffffffffffffffffff86166000908152602091825260408082206fffffffffffffffffffffffffffffffff80891684529352902080549092501615610d245780546040517ffda8d4740000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8083166004830152700100000000000000000000000000000000909204909116602482015260440161054c565b6fffffffffffffffffffffffffffffffff828116700100000000000000000000000000000000810285831690811784556040805173ffffffffffffffffffffffffffffffffffffffff8a168152938816602085015283015260608201527fb6c4db7a42dfab7553d15168f88363d3af3f85739e3ec0d1f30c96028fe32f8f9060800160405180910390a15050505050565b6000610dbf612a7d565b5473ffffffffffffffffffffffffffffffffffffffff16919050565b6000610de5612438565b8054909150600114610e53576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e435900000000000000000000000000000000000000000000604482015260640161054c565b60028155816fffffffffffffffffffffffffffffffff16600003610ea3576040517fe11543e100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610ead61247d565b33600090815260209190915260409020600381015490915073ffffffffffffffffffffffffffffffffffffffff16610f11576040517ffa267cd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610f1b6124bd565b9050836fffffffffffffffffffffffffffffffff16816000016000828254610f439190613de0565b9091555050815484908390600090610f6e9084906fffffffffffffffffffffffffffffffff16613db0565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550610fae3383612563565b610fcd33856fffffffffffffffffffffffffffffffff16600080612abd565b50506001905550565b6000610dbf6040518060400160405280600781526020017f61697264726f70000000000000000000000000000000000000000000000000008152506123bb565b60006110566040518060400160405280600681526020017f6c6f636b6564000000000000000000000000000000000000000000000000000081525061233e565b54919050565b73ffffffffffffffffffffffffffffffffffffffff81166110d9576040517fb14df79c00000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f746f000000000000000000000000000000000000000000000000000000000000604482015260640161054c565b60006110e361247d565b33600090815260209190915260409020600381015490915073ffffffffffffffffffffffffffffffffffffffff16611147576040517ffa267cd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600381015473ffffffffffffffffffffffffffffffffffffffff9081169083160361119e576040517fa7b752f200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006111a8612bc3565b60038301549091506000906111d490839073ffffffffffffffffffffffffffffffffffffffff16612c16565b600384015484549192506112369173ffffffffffffffffffffffffffffffffffffffff9091169061122b9070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1684613d9d565b849190610100612cba565b6003830154835473ffffffffffffffffffffffffffffffffffffffff9091169033907f33161cf2da28d747be9df136b6f3729390298494947268743193c53d73d3c2e0906112ca9070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613df3565b60405190815260200160405180910390a360006112e78386612c16565b6040805160c08101825286546fffffffffffffffffffffffffffffffff8082168352700100000000000000000000000000000000918290048116602084015260018901548082169484019490945292049091166060820152600286015473ffffffffffffffffffffffffffffffffffffffff908116608083015260038701541660a082015290915060009061137b90612e23565b90506113968661138b8385613de0565b869190610100612cba565b84546fffffffffffffffffffffffffffffffff80831670010000000000000000000000000000000002911617855560038501805473ffffffffffffffffffffffffffffffffffffffff88167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116811790915560405133907f33161cf2da28d747be9df136b6f3729390298494947268743193c53d73d3c2e09061143e9085815260200190565b60405180910390a3505050505050565b6000611458612438565b80549091506001146114c6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e435900000000000000000000000000000000000000000000604482015260640161054c565b6002815573ffffffffffffffffffffffffffffffffffffffff821615806114fd57506fffffffffffffffffffffffffffffffff8316155b15611564576040517f3205f1c600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201526fffffffffffffffffffffffffffffffff8416602482015260440161054c565b61156e828461090a565b6fffffffffffffffffffffffffffffffff166000036115b9576040517fc985ca0800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006115c361247d565b33600090815260209190915260409020600381015490915073ffffffffffffffffffffffffffffffffffffffff16611627576040517ffa267cd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600281015473ffffffffffffffffffffffffffffffffffffffff161561164f5761164f6127de565b60028101805473ffffffffffffffffffffffffffffffffffffffff85167fffffffffffffffffffffffff00000000000000000000000000000000000000009091161790556001810180546fffffffffffffffffffffffffffffffff8087167001000000000000000000000000000000000291161790556116d0338486612ec2565b6116da3382612563565b50600190555050565b505050565b6116f0610db5565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611754576040517fe927ce0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6117d56117956040518060400160405280600781526020017f61697264726f70000000000000000000000000000000000000000000000000008152506123bb565b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8416179055565b60405173ffffffffffffffffffffffffffffffffffffffff821681527fabe40da5dd07060349f485f71bd70d796ba49859153e786fe58a1d11f86c4ac99060200160405180910390a150565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915261185b61247d565b73ffffffffffffffffffffffffffffffffffffffff92831660009081526020918252604090819020815160c08101835281546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000091829004811695830195909552600183015480861694830194909452909204909216606082015260028201548416608082015260039091015490921660a08301525090565b6119386040518060400160405280600781526020017f61697264726f70000000000000000000000000000000000000000000000000008152506123bb565b5473ffffffffffffffffffffffffffffffffffffffff163314611987576040517fab04925b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611991612438565b80549091506001146119ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e435900000000000000000000000000000000000000000000604482015260640161054c565b60028155826fffffffffffffffffffffffffffffffff16600003611a4f576040517fe11543e100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8416611ace576040517fb14df79c00000000000000000000000000000000000000000000000000000000815260040161054c9060208082526004908201527f7573657200000000000000000000000000000000000000000000000000000000604082015260600190565b6000611ad861247d565b73ffffffffffffffffffffffffffffffffffffffff80871660009081526020929092526040909120600381015490925016611b2057611b1b858560008087612f60565b611c3b565b600381015473ffffffffffffffffffffffffffffffffffffffff848116911614611b9c5760038101546040517fa044d4d100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8086166004830152909116602482015260440161054c565b6000611ba66124bd565b9050846fffffffffffffffffffffffffffffffff16816000016000828254611bce9190613de0565b9091555050815485908390600090611bf99084906fffffffffffffffffffffffffffffffff16613db0565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550611c398683612563565b505b611c5a33856fffffffffffffffffffffffffffffffff16600080612abd565b50600181555b50505050565b6000611c70612438565b8054909150600114611cde576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e435900000000000000000000000000000000000000000000604482015260640161054c565b60028155611cea6127de565b60019055565b50565b611cfb6121cf565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611d5f576040517f88fe7b4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d67611016565b600114611da0576040517fedc4227400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611de7611de16040518060400160405280600681526020017f6c6f636b6564000000000000000000000000000000000000000000000000000081525061233e565b60029055565b6040517f15fa93f6df3cbaf0b9ddb555fa798ba412e2bbc86e60aa62d8aade4c3052cd6290600090a1565b6000611e1c612438565b8054909150600114611e8a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e435900000000000000000000000000000000000000000000604482015260640161054c565b60028155846fffffffffffffffffffffffffffffffff16600003611eda576040517fe11543e100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ee73386868686612f60565b611f0533866fffffffffffffffffffffffffffffffff168587612abd565b600181555b5050505050565b611f196121cf565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611f7d576040517f88fe7b4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116611ffa576040517f04d9a6d200000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f74696d656c6f636b000000000000000000000000000000000000000000000000604482015260640161054c565b611cf06117956040518060400160405280600881526020017f74696d656c6f636b0000000000000000000000000000000000000000000000008152506123bb565b600080612046612a3d565b73ffffffffffffffffffffffffffffffffffffffff85166000908152602091825260408082206fffffffffffffffffffffffffffffffff8088168452935290205470010000000000000000000000000000000090041691505092915050565b6120ad6121cf565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612111576040517f88fe7b4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811661218e576040517f04d9a6d200000000000000000000000000000000000000000000000000000000815260206004820152600760248201527f6d616e6167657200000000000000000000000000000000000000000000000000604482015260640161054c565b611cf06117956040518060400160405280600781526020017f6d616e61676572000000000000000000000000000000000000000000000000008152506123bb565b6000610dbf6131b4565b6000806121e4612bc3565b90506121f18185856131f4565b949350505050565b600080612204612bc3565b905061223e86866122357f0000000000000000000000000000000000000000000000000000000000030d4043613d9d565b84929190613265565b9695505050505050565b6032811115612283576040517f48a3bbbc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b818110156116e357600061229861247d565b60008585858181106122ac576122ac613e3f565b90506020020160208101906122c19190613a44565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905061232d84848481811061231257612312613e3f565b90506020020160208101906123279190613a44565b82612563565b5061233781613e6e565b9050612286565b6000807fec13d6d12b88433319b64e1065a96ea19cd330ef6603f5f6fb685dde3959a320905060008184604051602001612379929190613eca565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190528051602090910120949350505050565b9055565b6000807f421683f821a0574472445355be6d2b769119e8515f8376a1d7878523dfdecf7b905060008184604051602001612379929190613eca565b81547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff91909116179055565b60006124786040518060400160405280600781526020017f656e74657265640000000000000000000000000000000000000000000000000081525061233e565b905090565b60006124786040518060400160405280600d81526020017f726567697374726174696f6e73000000000000000000000000000000000000008152506132f7565b60006124786040518060400160405280600781526020017f62616c616e63650000000000000000000000000000000000000000000000000081525061233e565b600081600001516fffffffffffffffffffffffffffffffff1682604001516fffffffffffffffffffffffffffffffff160361253a57506000919050565b6040820151825161254b9190613ef0565b6fffffffffffffffffffffffffffffffff1692915050565b600061256d612bc3565b600383015490915060009061259990839073ffffffffffffffffffffffffffffffffffffffff16612c16565b6040805160c08101825285546fffffffffffffffffffffffffffffffff8082168352700100000000000000000000000000000000918290048116602084015260018801548082169484019490945292049091166060820152600285015473ffffffffffffffffffffffffffffffffffffffff908116608083015260038601541660a082015290915060009061262d90612e23565b84549091506000906126659070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1683613f19565b90508060000361267757505050505050565b60008113156126ae5760038501546126a99073ffffffffffffffffffffffffffffffffffffffff1661138b8386613de0565b61275d565b6126d8817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613df3565b8311156127325760038501546126a99073ffffffffffffffffffffffffffffffffffffffff16612728837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613df3565b61138b9086613d9d565b600385015461275d90859073ffffffffffffffffffffffffffffffffffffffff166000610100612cba565b84546fffffffffffffffffffffffffffffffff808416700100000000000000000000000000000000029116178555600385015460405173ffffffffffffffffffffffffffffffffffffffff918216918816907f33161cf2da28d747be9df136b6f3729390298494947268743193c53d73d3c2e09061143e9085815260200190565b60006127e861247d565b33600090815260209190915260409020600281015490915073ffffffffffffffffffffffffffffffffffffffff1661289a57600281015460018201546040517f3205f1c600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909216600483015270010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16602482015260440161054c565b6002810154600180830154604080516020810182526000815290517ff242432a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9094169363f242432a9361293093309333937001000000000000000000000000000000009092046fffffffffffffffffffffffffffffffff169291600401613f83565b600060405180830381600087803b15801561294a57600080fd5b505af115801561295e573d6000803e3d6000fd5b5050506002820180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055506001810180546fffffffffffffffffffffffffffffffff169055611cf03382612563565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001790526116e390849061336a565b60006124786040518060400160405280600b81526020017f6d756c7469706c69657273000000000000000000000000000000000000000000815250613476565b60006124786040518060400160405280600781526020017f6d616e61676572000000000000000000000000000000000000000000000000008152506123bb565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8581166004830152306024830152604482018590527f000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf16906323b872dd906064016020604051808303816000875af1158015612b58573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b7c9190613fda565b5073ffffffffffffffffffffffffffffffffffffffff821615801590612bb357506fffffffffffffffffffffffffffffffff811615155b15611c6057611c60848383612ec2565b6040805180820190915260608152600060208201526124786040518060400160405280600b81526020017f766f74696e67506f7765720000000000000000000000000000000000000000008152506134ac565b600080612c24846020015190565b73ffffffffffffffffffffffffffffffffffffffff841660009081526020919091526040812080549092506fffffffffffffffffffffffffffffffff1690819003612c7457600092505050610a14565b6000612caf83612c85600185613d9d565b016001015460c081901c9177ffffffffffffffffffffffffffffffffffffffffffffffff90911690565b979650505050505050565b77ffffffffffffffffffffffffffffffffffffffffffffffff821115612d3c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f4f6f420000000000000000000000000000000000000000000000000000000000604482015260640161054c565b6000612d49856020015190565b73ffffffffffffffffffffffffffffffffffffffff851660009081526020829052604081208054929350914360c01b9186831791608081901c916fffffffffffffffffffffffffffffffff909116908115612db157612dad86612c85600185613d9d565b5090505b81438203612dcb57612dc4600184613d9d565b9050612df1565b88612dd68585613d9d565b10612df157612df184612de881613e6e565b955085896134e5565b8481600189010155438214612e1557612e158785612e10866001613de0565b61350e565b505050505050505050505050565b60008082604001518360000151612e3a9190613ef0565b608084015190915073ffffffffffffffffffffffffffffffffffffffff1615801590612e7b575060608301516fffffffffffffffffffffffffffffffff1615155b1561254b576103e8612e958460800151856060015161090a565b612e9f9083613ffc565b612ea9919061405f565b6fffffffffffffffffffffffffffffffff169392505050565b604080516020810182526000815290517ff242432a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84169163f242432a91612f299187913091879160019190600401613f83565b600060405180830381600087803b158015612f4357600080fd5b505af1158015612f57573d6000803e3d6000fd5b50505050505050565b6000612f6c838561090a565b9050806fffffffffffffffffffffffffffffffff16600003612fba576040517fc985ca0800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612fc46124bd565b90506000612fd061247d565b73ffffffffffffffffffffffffffffffffffffffff808a166000908152602092909252604090912060038101549092501615613038576040517f9e2b1ab100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84161561305a578361305c565b875b935060006103e861306d858a613ffc565b613077919061405f565b6fffffffffffffffffffffffffffffffff808216700100000000000000000000000000000000908102828c169081178655918a1602600185015560028401805473ffffffffffffffffffffffffffffffffffffffff808b167fffffffffffffffffffffffff000000000000000000000000000000000000000092831617909255600386018054928a16929091169190911790558454919250908490600090613120908490613de0565b9091555061313090508582613537565b8473ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff167f33161cf2da28d747be9df136b6f3729390298494947268743193c53d73d3c2e0836fffffffffffffffffffffffffffffffff166040516131a191815260200190565b60405180910390a3505050505050505050565b60006124786040518060400160405280600881526020017f74696d656c6f636b0000000000000000000000000000000000000000000000008152506123bb565b600080613202856020015190565b73ffffffffffffffffffffffffffffffffffffffff85166000908152602082905260408120805492935091608081901c916fffffffffffffffffffffffffffffffff909116906132558488838686613571565b96505050505050505b9392505050565b600080613273866020015190565b73ffffffffffffffffffffffffffffffffffffffff86166000908152602082905260408120805492935091608081901c916fffffffffffffffffffffffffffffffff90911690806132c7858a8a8787613571565b91509150838211156132e9576132de8483876134e5565b6132e985838561350e565b9a9950505050505050505050565b6000807ff162ab2a93c7e839fe65139803001cff34be31efd14d829854af05b9911b6e848360405160200161332d929190613eca565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209392505050565b60006133cc826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff1661374c9092919063ffffffff16565b8051909150156116e357808060200190518101906133ea9190613fda565b6116e3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161054c565b6000807fafcb3a8867bd263fd503fc00b0ffd9a453f69f3268e9d48926d67e3802a891d98360405160200161332d929190613eca565b60408051808201909152606081526000602082015260006134cc8361375b565b6040805180820190915293845260208401525090919050565b818311156134f257600080fd5b60018101835b83811015611f0a576000828201556001016134f8565b80821061351a57600080fd5b6fffffffffffffffffffffffffffffffff1660809190911b179055565b6000613541612bc3565b9050600061354f8285612c16565b9050611c608461122b6fffffffffffffffffffffffffffffffff861684613de0565b600080826000036135de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f756e696e697469616c697a656400000000000000000000000000000000000000604482015260640161054c565b858511156135eb57600080fd5b8284106135f757600080fd5b6000613604600185613d9d565b90508460005b8282146136a7576000600261361f8585613de0565b61362a906001613de0565b613634919061408e565b6001818d01015490915060c081901c9077ffffffffffffffffffffffffffffffffffffffffffffffff168b8203613675579296509194506137429350505050565b8b821015613691578a821015613689578293505b82945061369f565b61369c600184613d9d565b95505b50505061360a565b60018a8301015460c081901c9077ffffffffffffffffffffffffffffffffffffffffffffffff168a821115613738576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f536561726368204661696c757265000000000000000000000000000000000000604482015260640161054c565b9195509093505050505b9550959350505050565b60606121f18484600085613796565b6000807f7b1a68ec3e3284b167e69db1c622dcfa612281976b71d7e2d239dbe16a75891a905060008184604051602001612379929190613eca565b606082471015613828576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161054c565b843b613890576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161054c565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516138b991906140a2565b60006040518083038185875af1925050503d80600081146138f6576040519150601f19603f3d011682016040523d82523d6000602084013e6138fb565b606091505b5091509150612caf8282866060831561391557508161325e565b8251156139255782518084602001fd5b816040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161054c91906140be565b80356fffffffffffffffffffffffffffffffff8116811461397957600080fd5b919050565b60006020828403121561399057600080fd5b61325e82613959565b803573ffffffffffffffffffffffffffffffffffffffff8116811461397957600080fd5b600080604083850312156139d057600080fd5b6139d983613999565b91506139e760208401613959565b90509250929050565b60008060008060808587031215613a0657600080fd5b613a0f85613999565b9350613a1d60208601613959565b9250613a2b60408601613959565b9150613a3960608601613959565b905092959194509250565b600060208284031215613a5657600080fd5b61325e82613999565b60008060408385031215613a7257600080fd5b613a7b83613959565b91506139e760208401613999565b600080600060608486031215613a9e57600080fd5b613aa784613999565b9250613ab560208501613959565b9150613ac360408501613999565b90509250925092565b60008060008060808587031215613ae257600080fd5b613aeb85613959565b9350613af960208601613959565b9250613b0760408601613999565b9150613a3960608601613999565b60008060408385031215613b2857600080fd5b613b3183613999565b946020939093013593505050565b60008060008060608587031215613b5557600080fd5b613b5e85613999565b935060208501359250604085013567ffffffffffffffff80821115613b8257600080fd5b818701915087601f830112613b9657600080fd5b813581811115613ba557600080fd5b886020828501011115613bb757600080fd5b95989497505060200194505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080600080600060a08688031215613c0d57600080fd5b613c1686613999565b9450613c2460208701613999565b93506040860135925060608601359150608086013567ffffffffffffffff80821115613c4f57600080fd5b818801915088601f830112613c6357600080fd5b813581811115613c7557613c75613bc6565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715613cbb57613cbb613bc6565b816040528281528b6020848701011115613cd457600080fd5b8260208601602083013760006020848301015280955050505050509295509295909350565b60008060208385031215613d0c57600080fd5b823567ffffffffffffffff80821115613d2457600080fd5b818501915085601f830112613d3857600080fd5b813581811115613d4757600080fd5b8660208260051b8501011115613d5c57600080fd5b60209290920196919550909350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610a1457610a14613d6e565b6fffffffffffffffffffffffffffffffff818116838216019080821115613dd957613dd9613d6e565b5092915050565b80820180821115610a1457610a14613d6e565b808202600082127f800000000000000000000000000000000000000000000000000000000000000084141615613e2b57613e2b613d6e565b8181058314821517610a1457610a14613d6e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613e9f57613e9f613d6e565b5060010190565b60005b83811015613ec1578181015183820152602001613ea9565b50506000910152565b82815260008251613ee2816020850160208701613ea6565b919091016020019392505050565b6fffffffffffffffffffffffffffffffff828116828216039080821115613dd957613dd9613d6e565b8181036000831280158383131683831282161715613dd957613dd9613d6e565b60008151808452613f51816020860160208601613ea6565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600073ffffffffffffffffffffffffffffffffffffffff80881683528087166020840152506fffffffffffffffffffffffffffffffff8516604083015283606083015260a06080830152612caf60a0830184613f39565b600060208284031215613fec57600080fd5b8151801515811461325e57600080fd5b6fffffffffffffffffffffffffffffffff81811683821602808216919082811461402857614028613d6e565b505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006fffffffffffffffffffffffffffffffff8084168061408257614082614030565b92169190910492915050565b60008261409d5761409d614030565b500490565b600082516140b4818460208701613ea6565b9190910192915050565b60208152600061325e6020830184613f3956fea164736f6c6343000812000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf0000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000002c845ac4bac48a6cd1e1c88a84195b7d5805b8200000000000000000000000002c845ac4bac48a6cd1e1c88a84195b7d5805b82
-----Decoded View---------------
Arg [0] : token (address): 0xe020B01B6fbD83066aa2e8ee0CCD1eB8d9Cc70bF
Arg [1] : staleBlockLag (uint256): 200000
Arg [2] : timelock (address): 0x02C845ac4baC48A6CD1e1c88a84195B7d5805B82
Arg [3] : manager (address): 0x02C845ac4baC48A6CD1e1c88a84195B7d5805B82
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf
Arg [1] : 0000000000000000000000000000000000000000000000000000000000030d40
Arg [2] : 00000000000000000000000002c845ac4bac48a6cd1e1c88a84195b7d5805b82
Arg [3] : 00000000000000000000000002c845ac4bac48a6cd1e1c88a84195b7d5805b82
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 100.00% | $0.017518 | 3,327,932.8 | $58,297.83 |
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.