Overview
TokenID
24
Total Transfers
-
Market
Onchain Market Cap
$0.00
Circulating Supply Market Cap
-
Other Info
Token Contract
Loading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
Contract Source Code Verified (Exact Match)
Contract Name:
VestingVaultManager
Compiler Version
v0.8.25+commit.b61c2a91
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import {VaultManager, ERC721} from "../vault/VaultManager.sol"; /// @notice Vesting vault manager class. contract VestingVaultManager is VaultManager { /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* CONSTANTS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev The role flag for admin. uint256 public constant ADMIN_ROLE = _ROLE_0; /// @dev Initial vesting mode. uint8 internal constant _VESTING_MODE_NONE = 0; /// @dev Either in cliff, or in regular vesting. uint8 internal constant _VESTING_MODE_REGULAR = 1; /// @dev In early vesting. uint8 internal constant _VESTING_MODE_EARLY = 2; /// @dev For safety, in case of a typo. uint32 internal constant _VESTING_DURATION_MAX = 365 days; /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* CUSTOM ERRORS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev One non-transferable vault per account. error Soulbound(); /// @dev The value is locked. error Locked(); /// @dev The operation is not allowed for the current vesting mode. error InvalidVestingMode(); /// @dev Cannot start early vesting when vesting has already started. error VestingAlreadyStarted(); /// @dev The vesting duration must be greater than zero and /// less than or equal to `_VESTING_DURATION_MAX`. error InvalidVestingDuration(); /// @dev Withdrawn too much. error OverWithdrawn(); /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* STORAGE */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Address of the vault's proxy. address public vaultProxy; /// @dev Whether the cliff duration is locked. bool public cliffDurationLocked; /// @dev Whether the vesting duration is locked. bool public vestingDurationLocked; /// @dev Whether the early vesting duration is locked. bool public earlyVestingDurationLocked; /// @dev Whether the vault's proxy is locked. bool public vaultProxyLocked; /// @dev The vesting mode. uint8 public vestingMode; /// @dev The start of the vesting period. uint32 public vestingStart; /// @dev The end of the vesting period. uint32 public vestingEnd; /// @dev The cliff duration. uint32 public cliffDuration; /// @dev The regular vesting duration. uint32 public vestingDuration; /// @dev The early vesting duration. uint32 public earlyVestingDuration; /// @dev Mapping of `holder` to their initially locked Asterix ERC20 amount. mapping(address => uint256) public initialLockedAsterixERC20Amount; /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* CONSTRUCTOR */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ constructor() { // Use an average of 30 days in a month. cliffDuration = 1 days * 30 * 6; // 6 months. vestingDuration = 1 days * 30 * 10; // 10 months. earlyVestingDuration = 1 days * 30 * 8; // 8 months; _initializeVaultManager( msg.sender == 0x0000000000FFe8B47B3e2130213B802212439497 ? tx.origin : msg.sender ); } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* VAULT OPERATIONS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Returns the amount of Asterix ERC20 that `holder` can currently withdraw. function withdrawableAsterixERC20(address holder) public view virtual returns (uint256 result) { address vault = vaultOf(holder); if (vault == address(0)) return 0; // `vaultBalance > requiredBalance ? vaultBalance - requiredBalance : 0`. return _zeroFloorSub(_balanceOf(ASTERIX, vault), requiredAsterixERC20Balance(holder)); } /// @dev Returns the required balance in the vault of `holder`. function requiredAsterixERC20Balance(address holder) public view virtual returns (uint256 result) { // If the cliff has not started, the `holder` is still allowed to withdraw // all of their Asterix balance in the vault, in case of an over deposit by mistake. if (vestingMode != _VESTING_MODE_NONE) { result = initialLockedAsterixERC20Amount[holder]; // Within uint96. uint256 end = vestingEnd; uint256 start = vestingStart; if (block.timestamp >= start) { unchecked { // `((end > timestamp ? end - time : 0) * result) / (end - start)`. result = _rawDiv(_zeroFloorSub(end, block.timestamp) * result, end - start); } } } } /// @dev Override to return the vault proxy for use in `_createVault`. function _vaultProxy() internal view virtual override(VaultManager) returns (address) { return vaultProxy; } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* ADMIN OPERATIONS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Locks the vault proxy. function lockVaultProxy() public virtual onlyOwnerOrRoles(ADMIN_ROLE) { vaultProxyLocked = true; } /// @dev Sets the vault proxy. function setVaultProxy(address proxy) public virtual onlyOwnerOrRoles(ADMIN_ROLE) { if (vaultProxyLocked) revert Locked(); vaultProxy = proxy; } /// @dev Locks the cliff duration. function lockCliffDuration() public virtual onlyOwnerOrRoles(ADMIN_ROLE) { cliffDurationLocked = true; } /// @dev Set the cliff duration. function setCliffDuration(uint32 value) public virtual onlyOwnerOrRoles(ADMIN_ROLE) { if (cliffDurationLocked) revert Locked(); if (value == 0 || value > _VESTING_DURATION_MAX) revert InvalidVestingDuration(); cliffDuration = value; } /// @dev Locks the vesting duration. function lockVestingDuration() public virtual onlyOwnerOrRoles(ADMIN_ROLE) { vestingDurationLocked = true; } /// @dev Sets the vesting duration. function setVestingDuration(uint32 value) public virtual onlyOwnerOrRoles(ADMIN_ROLE) { if (vestingDurationLocked) revert Locked(); if (value == 0 || value > _VESTING_DURATION_MAX) revert InvalidVestingDuration(); vestingDuration = value; } /// @dev Locks the early vesting duration. function lockEarlyVestingDuration() public virtual onlyOwnerOrRoles(ADMIN_ROLE) { earlyVestingDurationLocked = true; } /// @dev Sets the early vesting duration. function setEarlyVestingDuration(uint32 value) public virtual onlyOwnerOrRoles(ADMIN_ROLE) { if (earlyVestingDurationLocked) revert Locked(); if (value == 0 || value > _VESTING_DURATION_MAX) revert InvalidVestingDuration(); earlyVestingDuration = value; } /// @dev Sets the cliff, which will lead to the regular vesting. function startCliff() public virtual onlyOwnerOrRoles(ADMIN_ROLE) { if (vestingMode != _VESTING_MODE_NONE) revert InvalidVestingMode(); uint32 start = uint32(block.timestamp) + cliffDuration; vestingMode = _VESTING_MODE_REGULAR; vestingStart = start; vestingEnd = start + vestingDuration; cliffDurationLocked = true; vestingDurationLocked = true; } /// @dev Starts the early vesting period. function startEarlyVesting() public virtual onlyOwnerOrRoles(ADMIN_ROLE) { if (vestingMode != _VESTING_MODE_REGULAR) revert InvalidVestingMode(); if (block.timestamp >= vestingStart) revert VestingAlreadyStarted(); vestingMode = _VESTING_MODE_EARLY; vestingStart = uint32(block.timestamp); vestingEnd = uint32(block.timestamp) + earlyVestingDuration; earlyVestingDurationLocked = true; } /// @dev Directly resets the vesting state for testing purposes. function directResetVestingState(uint8 mode, uint32 start, uint32 end) public onlyOwnerOrRoles(ADMIN_ROLE) { _requireOnlyTestnet(); require(end >= start + 1, "`end` must be >= `start + 1`."); vestingMode = mode; vestingStart = start; vestingEnd = end; cliffDurationLocked = false; vestingDurationLocked = false; earlyVestingDurationLocked = false; } /// @dev Directly resets the vesting state for testing purposes. function directResetVestingState() public onlyOwnerOrRoles(ADMIN_ROLE) { _requireOnlyTestnet(); vestingMode = _VESTING_MODE_NONE; vestingStart = 0; vestingEnd = 0; cliffDurationLocked = false; vestingDurationLocked = false; earlyVestingDurationLocked = false; } /// @dev Directly resets the vesting state for testing purposes. function directSetInitialLockedAsterixERC20Amount(address holder, uint256 amount) public onlyOwnerOrRoles(ADMIN_ROLE) { _requireOnlyTestnet(); require(amount < 2 ** 96, "`amount` too big."); initialLockedAsterixERC20Amount[holder] = amount; } /// @dev Reverts if the chain is not Sepolia / Holesky testnet. function _requireOnlyTestnet() private view { require( block.chainid == 17000 || block.chainid == 11155111, "Only on Sepolia / Holesky testnet." ); } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* OVERRIDES */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Updates the initial locked amount for `holder`. function _afterDepositAsterixFor(address holder) internal virtual override(VaultManager) { uint256 currentBalance = _balanceOf(ASTERIX, vaultOf(holder)); // Before vesting starts, any deposit via the VestingVaultManager will update // `initialLockedAsterixERC20Amount` to the Asterix ERC20 balance of the vault. if ( vestingMode == _VESTING_MODE_NONE || block.timestamp < vestingStart || (currentBalance > initialLockedAsterixERC20Amount[holder] && msg.sender == owner()) ) { initialLockedAsterixERC20Amount[holder] = currentBalance; } } /// @dev Checks that there is enough ERC20 locked in the caller's vault. /// This override ensures that during the cliff and vesting phases, /// the `holder` cannot overWithdraw. function _afterWithdrawAsterix() internal virtual override(VaultManager) { address vault = vaultOf(msg.sender); if (vestingMode == _VESTING_MODE_NONE) { // Before vesting starts, any withdrawal via the VestingVaultManager will update // `initialLockedAsterixERC20Amount` to the Asterix ERC20 balance of the vault. initialLockedAsterixERC20Amount[msg.sender] = _balanceOf(ASTERIX, vault); } else { uint256 requiredBalance = requiredAsterixERC20Balance(msg.sender); if (_balanceOf(ASTERIX, vault) < requiredBalance) revert OverWithdrawn(); } } /// @dev Override for account-bound tokens. function setSingleTransferEnabled(bool) public virtual override(VaultManager) { if (true) revert Soulbound(); } /// @dev Override for account-bound tokens. function transferFrom(address, address, uint256) public payable virtual override(ERC721) { if (true) revert Soulbound(); } /// @dev Override for account-bound tokens. function approve(address, uint256) public payable virtual override(ERC721) { if (true) revert Soulbound(); } /// @dev Override for account-bound tokens. function getApproved(uint256) public view virtual override(ERC721) returns (address) { return address(0); } /// @dev Override for account-bound tokens. function setApprovalForAll(address, bool) public virtual override(ERC721) { if (true) revert Soulbound(); } /// @dev Override for account-bound tokens. function isApprovedForAll(address, address) public view virtual override(ERC721) returns (bool) { return false; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import {ERC721} from "solady/tokens/ERC721.sol"; import {Ownable, OwnableRoles} from "solady/auth/OwnableRoles.sol"; import {LibERC6551} from "solady/accounts/LibERC6551.sol"; import {LibString} from "solady/utils/LibString.sol"; import {ReentrancyGuard} from "soledge/utils/ReentrancyGuard.sol"; /// @notice ERC6551 vault manager class. abstract contract VaultManager is ERC721, OwnableRoles, ReentrancyGuard { /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* CONSTANTS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Address of the Asterix base ERC20. address public constant ASTERIX = 0x0000000000ca73A6df4C58b84C5B4b847FE8Ff39; /// @dev Address of the Asterix mirror ERC721. address public constant ASTERIX_MIRROR = 0x0000000000c26FAbFe894D13233d5ec73F61cc72; /// @dev The role flag for a metadata setter. uint256 public constant METADATA_SETTER_ROLE = _ROLE_31; /// @dev The scalar of ETH and most ERC20s. uint256 internal constant _WAD = 10 ** 18; /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* CUSTOM ERRORS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev An account can only have one vault at a time. error OnlyOneVaultPerAccount(); /// @dev The vault owner has not enabled it for a single transfer. error SingleTransferNotEnabled(); /// @dev Cannot double initialize the vault manager. error VaultManagerAlreadyInitialized(); /// @dev A vault has not been created yet for the caller. error VaultHasNotBeenCreated(); /// @dev The vault proxy is the zero address. error VaultProxyIsZeroAddress(); /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* EVENTS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Emitted when a vault is created. event VaultCreated(address indexed owner, uint256 indexed tokenId, address indexed vault); /// @dev The vault has been enabled / disabled for transfer. event SingleTransferEnabledSet(uint256 indexed tokenId, bool enabled); /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* STORAGE */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev For ERC721 tokenURI. address internal _tokenURIRenderer; /// @dev ERC721 name. string internal _name; /// @dev ERC721 symbol. string internal _symbol; /// @dev For ERC721 tokenURI. string internal _baseURI; /// @dev The current number of vaults in existence. /// The token IDs of vaults are auto-incremented, starting from 1. uint64 public totalVaults; /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* INITIALIZER */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev This MUST be called in the subclass' constructor. function _initializeVaultManager(address deployer) internal virtual { _initializeOwner(deployer); _grantRoles(deployer, METADATA_SETTER_ROLE); } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* METADATA */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev ERC721 name. function name() public view override(ERC721) returns (string memory) { return _name; } /// @dev ERC721 symbol. function symbol() public view override(ERC721) returns (string memory) { return _symbol; } /// @dev ERC721 tokenURI. /// Note: This function directly returns the string via assembly. function tokenURI(uint256 id) public view override(ERC721) returns (string memory result) { if (!_exists(id)) revert TokenDoesNotExist(); address renderer = _tokenURIRenderer; // If `renderer` is non-zero, use it instead of `_baseURI`. if (renderer != address(0)) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) mstore(0x00, 0xc87b56dd) // `tokenURI(uint256)`. mstore(0x20, id) if iszero(staticcall(gas(), renderer, 0x1c, 0x24, 0x00, 0x00)) { returndatacopy(result, 0x00, returndatasize()) revert(result, returndatasize()) } returndatacopy(0x00, 0x00, 0x20) // Copy the offset of the string in returndata. returndatacopy(result, mload(0x00), 0x20) // Copy the length of the string. returndatacopy(add(result, 0x20), add(mload(0x00), 0x20), mload(result)) // Copy the string. let end := add(add(result, 0x20), mload(result)) mstore(end, 0) // Zeroize the word after the string. mstore(0x40, add(end, 0x20)) // Allocate memory. } } else if (bytes(_baseURI).length != 0) { result = LibString.replace(_baseURI, "{id}", LibString.toString(id)); } } /// @dev For ERC721 trackers. function totalSupply() public view virtual returns (uint256) { return totalVaults; } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* METADATA SETTER FUNCTIONS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Sets the base URI for the ERC721 token URI. function setBaseURI(string calldata uri) public onlyOwnerOrRoles(METADATA_SETTER_ROLE) { _baseURI = uri; } /// @dev Sets the token URI renderer contract. function setTokenURIRenderer(address renderer) public onlyOwnerOrRoles(METADATA_SETTER_ROLE) { _tokenURIRenderer = renderer; } /// @dev Sets the ERC721 name and symbol. function setNameAndSymbol(string calldata name_, string calldata symbol_) public onlyOwnerOrRoles(METADATA_SETTER_ROLE) { _name = name_; _symbol = symbol_; } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* CONFIGURABLES */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Hook to override, which is called after any Asterix deposit. /// This is used to check if there's an over withdrawal of Asterix tokens, /// for used in vesting. function _afterDepositAsterixFor(address owner) internal virtual {} /// @dev Hook to override, which is called after any Asterix withdraw. /// This is used to check if there's an over withdrawal of Asterix tokens, /// for used in vesting. function _afterWithdrawAsterix() internal virtual {} /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* ASTERIX VAULT OPERATIONS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Creates a vault for `holder`. function createVaultFor(address holder) public virtual nonReentrant returns (address vault) { vault = _createVault(holder); } /// @dev Allows the caller to create a vault. function createVault() public virtual returns (address vault) { vault = createVaultFor(msg.sender); } /// @dev Deposits `amount` of the caller's Asterix ERC20 tokens into the vault of `holder`. /// A vault will be lazily created for `holder` if it does not exists. function depositAsterixERC20For(address holder, uint256 amount) public virtual nonReentrant returns (address vault) { vault = _createVault(holder); _depositBaseERC20ToVault(vault, ASTERIX, msg.sender, amount); _afterDepositAsterixFor(holder); } /// @dev Deposits `amount` of the caller's Asterix ERC20 tokens into the caller's vault. /// A vault will be lazily created for `holder` if it does not exists. function depositAsterixERC20(uint256 amount) public virtual returns (address vault) { vault = depositAsterixERC20For(msg.sender, amount); } /// @dev Deposits `tokenIds` of the caller's Asterix ERC721 tokens into the vault of `holder`. /// A vault will be lazily created for `holder` if it does not exists. function depositAsterixMirrorERC721For(address holder, uint256[] calldata tokenIds) public virtual nonReentrant returns (address vault) { vault = _createVault(holder); for (uint256 i; i < tokenIds.length; ++i) { _depositMirrorERC721ToVault(vault, ASTERIX_MIRROR, msg.sender, _get(tokenIds, i)); } _afterDepositAsterixFor(holder); } /// @dev Deposits `tokenIds` of the caller's Asterix ERC721 tokens into the caller's vault. /// A vault will be lazily created for the caller if it does not exists. function depositAsterixMirrorERC721(uint256[] calldata tokenIds) public virtual returns (address vault) { vault = depositAsterixMirrorERC721For(msg.sender, tokenIds); } /// @dev Withdraws `amount` Asterix ERC20 tokens from the caller's vault to `to`. function withdrawAsterixERC20To(address to, uint256 amount) public virtual nonReentrant returns (address vault) { vault = vaultOf(msg.sender); // Must use `msg.sender`. if (vault == address(0)) revert VaultHasNotBeenCreated(); _withdrawBaseERC20FromVault(vault, ASTERIX, to, amount); _afterWithdrawAsterix(); } /// @dev Withdraws `amount` Asterix ERC20 tokens from the caller's vault to the caller. function withdrawAsterixERC20(uint256 amount) public virtual returns (address vault) { vault = withdrawAsterixERC20To(msg.sender, amount); } /// @dev Withdraws `tokenIds` Asterix ERC721 tokens from the caller's vault to `to`. function withdrawAsterixMirrorERC721To(address to, uint256[] calldata tokenIds) public virtual nonReentrant returns (address vault) { vault = vaultOf(msg.sender); // Must use `msg.sender`. if (vault == address(0)) revert VaultHasNotBeenCreated(); for (uint256 i; i < tokenIds.length; ++i) { _withdrawMirrorERC721FromVault(vault, ASTERIX_MIRROR, to, _get(tokenIds, i)); } _afterWithdrawAsterix(); } /// @dev Withdraws `tokenIds` Asterix ERC721 tokens from the caller's vault to the caller. function withdrawAsterixMirrorERC721(uint256[] calldata tokenIds) public virtual returns (address vault) { vault = withdrawAsterixMirrorERC721To(msg.sender, tokenIds); } /// @dev Returns the maximum amount of Asterix ERC20 that can be withdrawn /// by the `holder` without burning any ERC721s in their vault. /// If the `holder` does not have a vault, the returned value is zero. function maxAsterixERC20WithdrawableWithoutBurningERC721s(address holder) public view virtual returns (uint256) { address vault = vaultOf(holder); if (vault == address(0)) return 0; unchecked { return _balanceOf(ASTERIX, vault) - _balanceOf(ASTERIX_MIRROR, vault) * _WAD; } } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* GENERAL VAULT OPERATIONS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Override to return a valid vault proxy address. function _vaultProxy() internal view virtual returns (address) {} /// @dev Creates a vault for the `owner`. /// If a vault has already been created, returns the current vault held by the `owner`. function _createVault(address owner) internal virtual returns (address vault) { vault = address(uint160(_getAux(owner))); if (vault != address(0)) return vault; unchecked { address proxy = _vaultProxy(); if (proxy == address(0)) revert VaultProxyIsZeroAddress(); uint256 tokenId = ++totalVaults; // Overflow of uint64 is impractical. _mintAndSetExtraDataUnchecked(owner, tokenId, 0); // This calls `createAccount` on the canonical ERC6551 registry, // which deploys a ERC6551 minimal bytecode proxy with // `implementation`, `salt`, `chainId`, `tokenContract` baked into the bytecode. vault = LibERC6551.createAccount( proxy, // `implementation`, which is a proxy. So we have a proxy to a proxy. bytes32(0), // `salt`. Always zero, for simplicity. block.chainid, // `chainId`. address(this), // `tokenContract`. tokenId // `tokenId`. ); emit VaultCreated(owner, tokenId, vault); // Use Solady's ERC721 data hitchhiking functions to store the vault's address. _setAux(owner, uint160(vault)); } } /// @dev Calls `setSkipNFT(bool)` on `baseERC20` for the `vault`. /// This function assumes `vault` has been created, and is a valid vault. function _setSkipNFT(address vault, address baseERC20, bool status) internal virtual { // `setSkipNFT(bool)`. _execute(vault, baseERC20, 0x2a6a935d, _toUint(status), 0, 0); } /// @dev Transfers `amount` of `baseERC20` from `from` to the `vault`. /// Requires that at least `amount` of `baseERC20` has been approved by `from` /// to be managed by this contract. /// This function assumes that `baseERC20` exists, and is a valid ERC20. /// This function assumes `vault` has been created, and is a valid vault. function _depositBaseERC20ToVault( address vault, address baseERC20, address from, uint256 amount ) internal virtual { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, amount) // Store the `amount` argument. mstore(0x40, vault) // Store the `to` argument. mstore(0x2c, shl(96, from)) // Store the `from` argument. mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`. // Perform the transfer, reverting upon failure. if iszero(call(gas(), baseERC20, 0, 0x1c, 0x64, 0x00, 0x20)) { returndatacopy(m, 0x00, returndatasize()) revert(m, returndatasize()) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Transfers `amount` of `baseERC20` from the `vault` to `to`. /// This function assumes `vault` has been created, and is a valid vault. /// /// Note: If the vault has 1 or more NFT tokens, we should revert if /// withdrawing will lead to the burning of any NFT token. function _withdrawBaseERC20FromVault( address vault, address baseERC20, address to, uint256 amount ) internal virtual { // `transfer(address,uint256)`. _execute(vault, baseERC20, 0xa9059cbb, uint160(to), uint160(amount), 0); } /// @dev Transfers `tokenId` of `mirrorERC721` from `from` to the `vault`. /// Requires that either `tokenId` has been approved by `from` to be managed /// by this contract, or this contract has been approved as an operator for `from`. /// This function assumes that `mirrorERC721` exists, and is a valid ERC721. /// This function assumes `vault` has been created, and is a valid vault. function _depositMirrorERC721ToVault( address vault, address mirrorERC721, address from, uint256 tokenId ) internal virtual { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, tokenId) // Store the `tokenId` argument. mstore(0x40, vault) // Store the `to` argument. mstore(0x2c, shl(96, from)) // Store the `from` argument. mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`. // Perform the transfer, reverting upon failure. if iszero(call(gas(), mirrorERC721, 0, 0x1c, 0x64, 0x00, 0x20)) { returndatacopy(m, 0x00, returndatasize()) revert(m, returndatasize()) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Transfers `tokenId` of `mirrorERC721` from the `vault` to `to`. /// This function assumes `vault` has been created, and is a valid vault. function _withdrawMirrorERC721FromVault( address vault, address mirrorERC721, address to, uint256 tokenId ) internal virtual { // `transferFrom(address,address,uint256)`. _execute(vault, mirrorERC721, 0x23b872dd, uint160(vault), uint160(to), tokenId); } /// @dev Helper function to call `execute` on the `vault`. /// This function assumes `vault` has been created, and is a valid vault. function _execute( address vault, address target, uint256 fnSelector, uint256 arg0, uint256 arg1, uint256 arg2 ) internal virtual { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) mstore(add(m, 0x14), target) mstore(m, 0x51945447000000000000000000000000) // `execute(address,uint256,bytes,uint8)`. mstore(add(m, 0x34), 0) // `value`. mstore(add(m, 0x54), 0x80) // Offset of `data`. mstore(add(m, 0x74), 0) // `operation`. mstore(add(m, 0x98), fnSelector) mstore(add(m, 0xb8), arg0) mstore(add(m, 0xd8), arg1) mstore(add(m, 0xf8), arg2) mstore(add(m, 0x94), 0x64) // `data.length`. if iszero(call(gas(), vault, 0, add(m, 0x10), 0x124, codesize(), 0x00)) { returndatacopy(m, 0x00, returndatasize()) revert(m, returndatasize()) } } } /// @dev Returns the amount of ERC20 or ERC721 `token` owned by `account`. /// Returns zero if the `token` does not exist. function _balanceOf(address token, address account) internal view returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x14, account) // Store the `account` argument. mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`. amount := mul( // The arguments of `mul` are evaluated from right to left. mload(0x20), and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20) ) ) } } /// @dev Returns the vault of the `owner`. /// Returns the zero address if owner does not have a vault. function vaultOf(address owner) public view virtual returns (address) { return address(uint160(_getAux(owner))); } /// @dev Returns the owner of the `vault`. /// Returns the zero address if the vault is not a valid vault. function vaultOwner(address vault) public view virtual returns (address result) { // The `tokenId` is from the ERC6551's proxy bytecode. uint256 tokenId = LibERC6551.tokenId(vault); address possibleOwner = _ownerOf(tokenId); // We must always validate against the vault's address stored in this contract. if (vaultOf(possibleOwner) == vault) result = possibleOwner; } /// @dev Returns the token ID of the `vault. /// Returns zero if the vault is not a valid vault. function vaultTokenId(address vault) public view virtual returns (uint256 result) { // The `tokenId` is from the ERC6551's proxy bytecode. uint256 tokenId = LibERC6551.tokenId(vault); address possibleOwner = _ownerOf(tokenId); // We must always validate against the vault's address stored in this contract. if (vaultOf(possibleOwner) == vault) result = tokenId; } /// @dev Returns the vault at `tokenId`. /// Returns the zero address if the vault has not been created. function vaultAt(uint256 tokenId) public view virtual returns (address) { return address(uint160(_getAux(_ownerOf(tokenId)))); } /// @dev Returns if the `tokenId` has been enabled for a single transfer. function singleTransferEnabled(uint256 tokenId) public view virtual returns (bool) { return _getExtraData(tokenId) != 0; } /// @dev Enable a single transfer for the caller's vault. function setSingleTransferEnabled(bool enabled) public virtual nonReentrant { address vault = vaultOf(msg.sender); if (vault == address(0)) revert VaultHasNotBeenCreated(); // `vault` is from this contract's storage, // so it's safe to get the `tokenId` from its immutable ERC6551 bytecode. uint256 tokenId = LibERC6551.tokenId(vault); // Use Solady's ERC721 data hitchhiking to store the enabled flag. _setExtraData(tokenId, uint96(_toUint(enabled))); emit SingleTransferEnabledSet(tokenId, enabled); } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* OVERRIDES */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Hook override to ensure that an account can at most hold /// one vault at any time. /// /// Note: This override does not support burning logic. function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override(ERC721) { if (_getAux(to) != 0) revert OnlyOneVaultPerAccount(); if (from != address(0)) { if (_getExtraData(tokenId) == 0) revert SingleTransferNotEnabled(); _setExtraData(tokenId, 0); // Reset the single token transfer to zero. _setAux(to, _getAux(from)); // Set `to.vault` to `from.vault`. _setAux(from, 0); // Set `from.vault` to `address(0)`. } } /// @dev Use the ownable to prevent double initialization. function _guardInitializeOwner() internal pure virtual override(Ownable) returns (bool) { return true; } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* INTERNAL / PRIVATE HELPERS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Returns `b ? 1 : 0`. function _toUint(bool b) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { result := iszero(iszero(b)) } } /// @dev Returns `max(0, x - y)`. function _zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := mul(gt(x, y), sub(x, y)) } } /// @dev Returns `y == 0 ? 0 : x / y`. function _rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := div(x, y) } } /// @dev Returns `a[i]`, without bounds checks. function _get(uint256[] calldata a, uint256 i) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { result := calldataload(add(a.offset, shl(5, i))) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Simple ERC721 implementation with storage hitchhiking. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC721.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC721/ERC721.sol) /// /// @dev Note: /// - The ERC721 standard allows for self-approvals. /// For performance, this implementation WILL NOT revert for such actions. /// Please add any checks with overrides if desired. /// - For performance, methods are made payable where permitted by the ERC721 standard. /// - The `safeTransfer` functions use the identity precompile (0x4) /// to copy memory internally. /// /// If you are overriding: /// - NEVER violate the ERC721 invariant: /// the balance of an owner MUST always be equal to their number of ownership slots. /// The transfer functions do not have an underflow guard for user token balances. /// - Make sure all variables written to storage are properly cleaned // (e.g. the bool value for `isApprovedForAll` MUST be either 1 or 0 under the hood). /// - Check that the overridden function is actually used in the function you want to /// change the behavior of. Much of the code has been manually inlined for performance. abstract contract ERC721 { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev An account can hold up to 4294967295 tokens. uint256 internal constant _MAX_ACCOUNT_BALANCE = 0xffffffff; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Only the token owner or an approved account can manage the token. error NotOwnerNorApproved(); /// @dev The token does not exist. error TokenDoesNotExist(); /// @dev The token already exists. error TokenAlreadyExists(); /// @dev Cannot query the balance for the zero address. error BalanceQueryForZeroAddress(); /// @dev Cannot mint or transfer to the zero address. error TransferToZeroAddress(); /// @dev The token must be owned by `from`. error TransferFromIncorrectOwner(); /// @dev The recipient's balance has overflowed. error AccountBalanceOverflow(); /// @dev Cannot safely transfer to a contract that does not implement /// the ERC721Receiver interface. error TransferToNonERC721ReceiverImplementer(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Emitted when token `id` is transferred from `from` to `to`. event Transfer(address indexed from, address indexed to, uint256 indexed id); /// @dev Emitted when `owner` enables `account` to manage the `id` token. event Approval(address indexed owner, address indexed account, uint256 indexed id); /// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens. event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved); /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`. uint256 private constant _TRANSFER_EVENT_SIGNATURE = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`. uint256 private constant _APPROVAL_EVENT_SIGNATURE = 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925; /// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`. uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE = 0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ownership data slot of `id` is given by: /// ``` /// mstore(0x00, id) /// mstore(0x1c, _ERC721_MASTER_SLOT_SEED) /// let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) /// ``` /// Bits Layout: /// - [0..159] `addr` /// - [160..255] `extraData` /// /// The approved address slot is given by: `add(1, ownershipSlot)`. /// /// See: https://notes.ethereum.org/%40vbuterin/verkle_tree_eip /// /// The balance slot of `owner` is given by: /// ``` /// mstore(0x1c, _ERC721_MASTER_SLOT_SEED) /// mstore(0x00, owner) /// let balanceSlot := keccak256(0x0c, 0x1c) /// ``` /// Bits Layout: /// - [0..31] `balance` /// - [32..255] `aux` /// /// The `operator` approval slot of `owner` is given by: /// ``` /// mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator)) /// mstore(0x00, owner) /// let operatorApprovalSlot := keccak256(0x0c, 0x30) /// ``` uint256 private constant _ERC721_MASTER_SLOT_SEED = 0x7d8825530a5a2e7a << 192; /// @dev Pre-shifted and pre-masked constant. uint256 private constant _ERC721_MASTER_SLOT_SEED_MASKED = 0x0a5a2e7a00000000; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC721 METADATA */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the token collection name. function name() public view virtual returns (string memory); /// @dev Returns the token collection symbol. function symbol() public view virtual returns (string memory); /// @dev Returns the Uniform Resource Identifier (URI) for token `id`. function tokenURI(uint256 id) public view virtual returns (string memory); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC721 */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the owner of token `id`. /// /// Requirements: /// - Token `id` must exist. function ownerOf(uint256 id) public view virtual returns (address result) { result = _ownerOf(id); /// @solidity memory-safe-assembly assembly { if iszero(result) { mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. revert(0x1c, 0x04) } } } /// @dev Returns the number of tokens owned by `owner`. /// /// Requirements: /// - `owner` must not be the zero address. function balanceOf(address owner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // Revert if the `owner` is the zero address. if iszero(owner) { mstore(0x00, 0x8f4eb604) // `BalanceQueryForZeroAddress()`. revert(0x1c, 0x04) } mstore(0x1c, _ERC721_MASTER_SLOT_SEED) mstore(0x00, owner) result := and(sload(keccak256(0x0c, 0x1c)), _MAX_ACCOUNT_BALANCE) } } /// @dev Returns the account approved to manage token `id`. /// /// Requirements: /// - Token `id` must exist. function getApproved(uint256 id) public view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) if iszero(shl(96, sload(ownershipSlot))) { mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. revert(0x1c, 0x04) } result := sload(add(1, ownershipSlot)) } } /// @dev Sets `account` as the approved account to manage token `id`. /// /// Requirements: /// - Token `id` must exist. /// - The caller must be the owner of the token, /// or an approved operator for the token owner. /// /// Emits an {Approval} event. function approve(address account, uint256 id) public payable virtual { _approve(msg.sender, account, id); } /// @dev Returns whether `operator` is approved to manage the tokens of `owner`. function isApprovedForAll(address owner, address operator) public view virtual returns (bool result) { /// @solidity memory-safe-assembly assembly { mstore(0x1c, operator) mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED) mstore(0x00, owner) result := sload(keccak256(0x0c, 0x30)) } } /// @dev Sets whether `operator` is approved to manage the tokens of the caller. /// /// Emits an {ApprovalForAll} event. function setApprovalForAll(address operator, bool isApproved) public virtual { /// @solidity memory-safe-assembly assembly { // Convert to 0 or 1. isApproved := iszero(iszero(isApproved)) // Update the `isApproved` for (`msg.sender`, `operator`). mstore(0x1c, operator) mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x30), isApproved) // Emit the {ApprovalForAll} event. mstore(0x00, isApproved) // forgefmt: disable-next-item log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator))) } } /// @dev Transfers token `id` from `from` to `to`. /// /// Requirements: /// /// - Token `id` must exist. /// - `from` must be the owner of the token. /// - `to` cannot be the zero address. /// - The caller must be the owner of the token, or be approved to manage the token. /// /// Emits a {Transfer} event. function transferFrom(address from, address to, uint256 id) public payable virtual { _beforeTokenTransfer(from, to, id); /// @solidity memory-safe-assembly assembly { // Clear the upper 96 bits. let bitmaskAddress := shr(96, not(0)) from := and(bitmaskAddress, from) to := and(bitmaskAddress, to) // Load the ownership data. mstore(0x00, id) mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, caller())) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let ownershipPacked := sload(ownershipSlot) let owner := and(bitmaskAddress, ownershipPacked) // Revert if the token does not exist, or if `from` is not the owner. if iszero(mul(owner, eq(owner, from))) { // `TokenDoesNotExist()`, `TransferFromIncorrectOwner()`. mstore(shl(2, iszero(owner)), 0xceea21b6a1148100) revert(0x1c, 0x04) } // Load, check, and update the token approval. { mstore(0x00, from) let approvedAddress := sload(add(1, ownershipSlot)) // Revert if the caller is not the owner, nor approved. if iszero(or(eq(caller(), from), eq(caller(), approvedAddress))) { if iszero(sload(keccak256(0x0c, 0x30))) { mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. revert(0x1c, 0x04) } } // Delete the approved address if any. if approvedAddress { sstore(add(1, ownershipSlot), 0) } } // Update with the new owner. sstore(ownershipSlot, xor(ownershipPacked, xor(from, to))) // Decrement the balance of `from`. { let fromBalanceSlot := keccak256(0x0c, 0x1c) sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1)) } // Increment the balance of `to`. { mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x1c) let toBalanceSlotPacked := add(sload(toBalanceSlot), 1) // Revert if `to` is the zero address, or if the account balance overflows. if iszero(mul(to, and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE))) { // `TransferToZeroAddress()`, `AccountBalanceOverflow()`. mstore(shl(2, iszero(to)), 0xea553b3401336cea) revert(0x1c, 0x04) } sstore(toBalanceSlot, toBalanceSlotPacked) } // Emit the {Transfer} event. log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id) } _afterTokenTransfer(from, to, id); } /// @dev Equivalent to `safeTransferFrom(from, to, id, "")`. function safeTransferFrom(address from, address to, uint256 id) public payable virtual { transferFrom(from, to, id); if (_hasCode(to)) _checkOnERC721Received(from, to, id, ""); } /// @dev Transfers token `id` from `from` to `to`. /// /// Requirements: /// /// - Token `id` must exist. /// - `from` must be the owner of the token. /// - `to` cannot be the zero address. /// - The caller must be the owner of the token, or be approved to manage the token. /// - If `to` refers to a smart contract, it must implement /// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. /// /// Emits a {Transfer} event. function safeTransferFrom(address from, address to, uint256 id, bytes calldata data) public payable virtual { transferFrom(from, to, id); if (_hasCode(to)) _checkOnERC721Received(from, to, id, data); } /// @dev Returns true if this contract implements the interface defined by `interfaceId`. /// See: https://eips.ethereum.org/EIPS/eip-165 /// This function call must use less than 30000 gas. function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) { /// @solidity memory-safe-assembly assembly { let s := shr(224, interfaceId) // ERC165: 0x01ffc9a7, ERC721: 0x80ac58cd, ERC721Metadata: 0x5b5e139f. result := or(or(eq(s, 0x01ffc9a7), eq(s, 0x80ac58cd)), eq(s, 0x5b5e139f)) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL QUERY FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns if token `id` exists. function _exists(uint256 id) internal view virtual returns (bool result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) result := iszero(iszero(shl(96, sload(add(id, add(id, keccak256(0x00, 0x20))))))) } } /// @dev Returns the owner of token `id`. /// Returns the zero address instead of reverting if the token does not exist. function _ownerOf(uint256 id) internal view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) result := shr(96, shl(96, sload(add(id, add(id, keccak256(0x00, 0x20)))))) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL DATA HITCHHIKING FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // For performance, no events are emitted for the hitchhiking setters. // Please emit your own events if required. /// @dev Returns the auxiliary data for `owner`. /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data. /// Auxiliary data can be set for any address, even if it does not have any tokens. function _getAux(address owner) internal view virtual returns (uint224 result) { /// @solidity memory-safe-assembly assembly { mstore(0x1c, _ERC721_MASTER_SLOT_SEED) mstore(0x00, owner) result := shr(32, sload(keccak256(0x0c, 0x1c))) } } /// @dev Set the auxiliary data for `owner` to `value`. /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data. /// Auxiliary data can be set for any address, even if it does not have any tokens. function _setAux(address owner, uint224 value) internal virtual { /// @solidity memory-safe-assembly assembly { mstore(0x1c, _ERC721_MASTER_SLOT_SEED) mstore(0x00, owner) let balanceSlot := keccak256(0x0c, 0x1c) let packed := sload(balanceSlot) sstore(balanceSlot, xor(packed, shl(32, xor(value, shr(32, packed))))) } } /// @dev Returns the extra data for token `id`. /// Minting, transferring, burning a token will not change the extra data. /// The extra data can be set on a non-existent token. function _getExtraData(uint256 id) internal view virtual returns (uint96 result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) result := shr(160, sload(add(id, add(id, keccak256(0x00, 0x20))))) } } /// @dev Sets the extra data for token `id` to `value`. /// Minting, transferring, burning a token will not change the extra data. /// The extra data can be set on a non-existent token. function _setExtraData(uint256 id, uint96 value) internal virtual { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let packed := sload(ownershipSlot) sstore(ownershipSlot, xor(packed, shl(160, xor(value, shr(160, packed))))) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL MINT FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Mints token `id` to `to`. /// /// Requirements: /// /// - Token `id` must not exist. /// - `to` cannot be the zero address. /// /// Emits a {Transfer} event. function _mint(address to, uint256 id) internal virtual { _beforeTokenTransfer(address(0), to, id); /// @solidity memory-safe-assembly assembly { // Clear the upper 96 bits. to := shr(96, shl(96, to)) // Load the ownership data. mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let ownershipPacked := sload(ownershipSlot) // Revert if the token already exists. if shl(96, ownershipPacked) { mstore(0x00, 0xc991cbb1) // `TokenAlreadyExists()`. revert(0x1c, 0x04) } // Update with the owner. sstore(ownershipSlot, or(ownershipPacked, to)) // Increment the balance of the owner. { mstore(0x00, to) let balanceSlot := keccak256(0x0c, 0x1c) let balanceSlotPacked := add(sload(balanceSlot), 1) // Revert if `to` is the zero address, or if the account balance overflows. if iszero(mul(to, and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE))) { // `TransferToZeroAddress()`, `AccountBalanceOverflow()`. mstore(shl(2, iszero(to)), 0xea553b3401336cea) revert(0x1c, 0x04) } sstore(balanceSlot, balanceSlotPacked) } // Emit the {Transfer} event. log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id) } _afterTokenTransfer(address(0), to, id); } /// @dev Mints token `id` to `to`, and updates the extra data for token `id` to `value`. /// Does NOT check if token `id` already exists (assumes `id` is auto-incrementing). /// /// Requirements: /// /// - `to` cannot be the zero address. /// /// Emits a {Transfer} event. function _mintAndSetExtraDataUnchecked(address to, uint256 id, uint96 value) internal virtual { _beforeTokenTransfer(address(0), to, id); /// @solidity memory-safe-assembly assembly { // Clear the upper 96 bits. to := shr(96, shl(96, to)) // Update with the owner and extra data. mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) sstore(add(id, add(id, keccak256(0x00, 0x20))), or(shl(160, value), to)) // Increment the balance of the owner. { mstore(0x00, to) let balanceSlot := keccak256(0x0c, 0x1c) let balanceSlotPacked := add(sload(balanceSlot), 1) // Revert if `to` is the zero address, or if the account balance overflows. if iszero(mul(to, and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE))) { // `TransferToZeroAddress()`, `AccountBalanceOverflow()`. mstore(shl(2, iszero(to)), 0xea553b3401336cea) revert(0x1c, 0x04) } sstore(balanceSlot, balanceSlotPacked) } // Emit the {Transfer} event. log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id) } _afterTokenTransfer(address(0), to, id); } /// @dev Equivalent to `_safeMint(to, id, "")`. function _safeMint(address to, uint256 id) internal virtual { _safeMint(to, id, ""); } /// @dev Mints token `id` to `to`. /// /// Requirements: /// /// - Token `id` must not exist. /// - `to` cannot be the zero address. /// - If `to` refers to a smart contract, it must implement /// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. /// /// Emits a {Transfer} event. function _safeMint(address to, uint256 id, bytes memory data) internal virtual { _mint(to, id); if (_hasCode(to)) _checkOnERC721Received(address(0), to, id, data); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL BURN FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Equivalent to `_burn(address(0), id)`. function _burn(uint256 id) internal virtual { _burn(address(0), id); } /// @dev Destroys token `id`, using `by`. /// /// Requirements: /// /// - Token `id` must exist. /// - If `by` is not the zero address, /// it must be the owner of the token, or be approved to manage the token. /// /// Emits a {Transfer} event. function _burn(address by, uint256 id) internal virtual { address owner = ownerOf(id); _beforeTokenTransfer(owner, address(0), id); /// @solidity memory-safe-assembly assembly { // Clear the upper 96 bits. by := shr(96, shl(96, by)) // Load the ownership data. mstore(0x00, id) mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by)) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let ownershipPacked := sload(ownershipSlot) // Reload the owner in case it is changed in `_beforeTokenTransfer`. owner := shr(96, shl(96, ownershipPacked)) // Revert if the token does not exist. if iszero(owner) { mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. revert(0x1c, 0x04) } // Load and check the token approval. { mstore(0x00, owner) let approvedAddress := sload(add(1, ownershipSlot)) // If `by` is not the zero address, do the authorization check. // Revert if the `by` is not the owner, nor approved. if iszero(or(iszero(by), or(eq(by, owner), eq(by, approvedAddress)))) { if iszero(sload(keccak256(0x0c, 0x30))) { mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. revert(0x1c, 0x04) } } // Delete the approved address if any. if approvedAddress { sstore(add(1, ownershipSlot), 0) } } // Clear the owner. sstore(ownershipSlot, xor(ownershipPacked, owner)) // Decrement the balance of `owner`. { let balanceSlot := keccak256(0x0c, 0x1c) sstore(balanceSlot, sub(sload(balanceSlot), 1)) } // Emit the {Transfer} event. log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, owner, 0, id) } _afterTokenTransfer(owner, address(0), id); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL APPROVAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns whether `account` is the owner of token `id`, or is approved to manage it. /// /// Requirements: /// - Token `id` must exist. function _isApprovedOrOwner(address account, uint256 id) internal view virtual returns (bool result) { /// @solidity memory-safe-assembly assembly { result := 1 // Clear the upper 96 bits. account := shr(96, shl(96, account)) // Load the ownership data. mstore(0x00, id) mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, account)) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let owner := shr(96, shl(96, sload(ownershipSlot))) // Revert if the token does not exist. if iszero(owner) { mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. revert(0x1c, 0x04) } // Check if `account` is the `owner`. if iszero(eq(account, owner)) { mstore(0x00, owner) // Check if `account` is approved to manage the token. if iszero(sload(keccak256(0x0c, 0x30))) { result := eq(account, sload(add(1, ownershipSlot))) } } } } /// @dev Returns the account approved to manage token `id`. /// Returns the zero address instead of reverting if the token does not exist. function _getApproved(uint256 id) internal view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) result := sload(add(1, add(id, add(id, keccak256(0x00, 0x20))))) } } /// @dev Equivalent to `_approve(address(0), account, id)`. function _approve(address account, uint256 id) internal virtual { _approve(address(0), account, id); } /// @dev Sets `account` as the approved account to manage token `id`, using `by`. /// /// Requirements: /// - Token `id` must exist. /// - If `by` is not the zero address, `by` must be the owner /// or an approved operator for the token owner. /// /// Emits a {Approval} event. function _approve(address by, address account, uint256 id) internal virtual { assembly { // Clear the upper 96 bits. let bitmaskAddress := shr(96, not(0)) account := and(bitmaskAddress, account) by := and(bitmaskAddress, by) // Load the owner of the token. mstore(0x00, id) mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by)) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let owner := and(bitmaskAddress, sload(ownershipSlot)) // Revert if the token does not exist. if iszero(owner) { mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. revert(0x1c, 0x04) } // If `by` is not the zero address, do the authorization check. // Revert if `by` is not the owner, nor approved. if iszero(or(iszero(by), eq(by, owner))) { mstore(0x00, owner) if iszero(sload(keccak256(0x0c, 0x30))) { mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. revert(0x1c, 0x04) } } // Sets `account` as the approved account to manage `id`. sstore(add(1, ownershipSlot), account) // Emit the {Approval} event. log4(codesize(), 0x00, _APPROVAL_EVENT_SIGNATURE, owner, account, id) } } /// @dev Approve or remove the `operator` as an operator for `by`, /// without authorization checks. /// /// Emits an {ApprovalForAll} event. function _setApprovalForAll(address by, address operator, bool isApproved) internal virtual { /// @solidity memory-safe-assembly assembly { // Clear the upper 96 bits. by := shr(96, shl(96, by)) operator := shr(96, shl(96, operator)) // Convert to 0 or 1. isApproved := iszero(iszero(isApproved)) // Update the `isApproved` for (`by`, `operator`). mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator)) mstore(0x00, by) sstore(keccak256(0x0c, 0x30), isApproved) // Emit the {ApprovalForAll} event. mstore(0x00, isApproved) log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, by, operator) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL TRANSFER FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Equivalent to `_transfer(address(0), from, to, id)`. function _transfer(address from, address to, uint256 id) internal virtual { _transfer(address(0), from, to, id); } /// @dev Transfers token `id` from `from` to `to`. /// /// Requirements: /// /// - Token `id` must exist. /// - `from` must be the owner of the token. /// - `to` cannot be the zero address. /// - If `by` is not the zero address, /// it must be the owner of the token, or be approved to manage the token. /// /// Emits a {Transfer} event. function _transfer(address by, address from, address to, uint256 id) internal virtual { _beforeTokenTransfer(from, to, id); /// @solidity memory-safe-assembly assembly { // Clear the upper 96 bits. let bitmaskAddress := shr(96, not(0)) from := and(bitmaskAddress, from) to := and(bitmaskAddress, to) by := and(bitmaskAddress, by) // Load the ownership data. mstore(0x00, id) mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by)) let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) let ownershipPacked := sload(ownershipSlot) let owner := and(bitmaskAddress, ownershipPacked) // Revert if the token does not exist, or if `from` is not the owner. if iszero(mul(owner, eq(owner, from))) { // `TokenDoesNotExist()`, `TransferFromIncorrectOwner()`. mstore(shl(2, iszero(owner)), 0xceea21b6a1148100) revert(0x1c, 0x04) } // Load, check, and update the token approval. { mstore(0x00, from) let approvedAddress := sload(add(1, ownershipSlot)) // If `by` is not the zero address, do the authorization check. // Revert if the `by` is not the owner, nor approved. if iszero(or(iszero(by), or(eq(by, from), eq(by, approvedAddress)))) { if iszero(sload(keccak256(0x0c, 0x30))) { mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. revert(0x1c, 0x04) } } // Delete the approved address if any. if approvedAddress { sstore(add(1, ownershipSlot), 0) } } // Update with the new owner. sstore(ownershipSlot, xor(ownershipPacked, xor(from, to))) // Decrement the balance of `from`. { let fromBalanceSlot := keccak256(0x0c, 0x1c) sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1)) } // Increment the balance of `to`. { mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x1c) let toBalanceSlotPacked := add(sload(toBalanceSlot), 1) // Revert if `to` is the zero address, or if the account balance overflows. if iszero(mul(to, and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE))) { // `TransferToZeroAddress()`, `AccountBalanceOverflow()`. mstore(shl(2, iszero(to)), 0xea553b3401336cea) revert(0x1c, 0x04) } sstore(toBalanceSlot, toBalanceSlotPacked) } // Emit the {Transfer} event. log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id) } _afterTokenTransfer(from, to, id); } /// @dev Equivalent to `_safeTransfer(from, to, id, "")`. function _safeTransfer(address from, address to, uint256 id) internal virtual { _safeTransfer(from, to, id, ""); } /// @dev Transfers token `id` from `from` to `to`. /// /// Requirements: /// /// - Token `id` must exist. /// - `from` must be the owner of the token. /// - `to` cannot be the zero address. /// - The caller must be the owner of the token, or be approved to manage the token. /// - If `to` refers to a smart contract, it must implement /// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. /// /// Emits a {Transfer} event. function _safeTransfer(address from, address to, uint256 id, bytes memory data) internal virtual { _transfer(address(0), from, to, id); if (_hasCode(to)) _checkOnERC721Received(from, to, id, data); } /// @dev Equivalent to `_safeTransfer(by, from, to, id, "")`. function _safeTransfer(address by, address from, address to, uint256 id) internal virtual { _safeTransfer(by, from, to, id, ""); } /// @dev Transfers token `id` from `from` to `to`. /// /// Requirements: /// /// - Token `id` must exist. /// - `from` must be the owner of the token. /// - `to` cannot be the zero address. /// - If `by` is not the zero address, /// it must be the owner of the token, or be approved to manage the token. /// - If `to` refers to a smart contract, it must implement /// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. /// /// Emits a {Transfer} event. function _safeTransfer(address by, address from, address to, uint256 id, bytes memory data) internal virtual { _transfer(by, from, to, id); if (_hasCode(to)) _checkOnERC721Received(from, to, id, data); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HOOKS FOR OVERRIDING */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Hook that is called before any token transfers, including minting and burning. function _beforeTokenTransfer(address from, address to, uint256 id) internal virtual {} /// @dev Hook that is called after any token transfers, including minting and burning. function _afterTokenTransfer(address from, address to, uint256 id) internal virtual {} /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PRIVATE HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns if `a` has bytecode of non-zero length. function _hasCode(address a) private view returns (bool result) { /// @solidity memory-safe-assembly assembly { result := extcodesize(a) // Can handle dirty upper bits. } } /// @dev Perform a call to invoke {IERC721Receiver-onERC721Received} on `to`. /// Reverts if the target does not support the function correctly. function _checkOnERC721Received(address from, address to, uint256 id, bytes memory data) private { /// @solidity memory-safe-assembly assembly { // Prepare the calldata. let m := mload(0x40) let onERC721ReceivedSelector := 0x150b7a02 mstore(m, onERC721ReceivedSelector) mstore(add(m, 0x20), caller()) // The `operator`, which is always `msg.sender`. mstore(add(m, 0x40), shr(96, shl(96, from))) mstore(add(m, 0x60), id) mstore(add(m, 0x80), 0x80) let n := mload(data) mstore(add(m, 0xa0), n) if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xc0), n)) } // Revert if the call reverts. if iszero(call(gas(), to, 0, add(m, 0x1c), add(n, 0xa4), m, 0x20)) { if returndatasize() { // Bubble up the revert if the call reverts. returndatacopy(m, 0x00, returndatasize()) revert(m, returndatasize()) } } // Load the returndata and compare it. if iszero(eq(mload(m), shl(224, onERC721ReceivedSelector))) { mstore(0x00, 0xd1a57ed6) // `TransferToNonERC721ReceiverImplementer()`. revert(0x1c, 0x04) } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import {Ownable} from "./Ownable.sol"; /// @notice Simple single owner and multiroles authorization mixin. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol) /// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173) /// for compatibility, the nomenclature for the 2-step ownership handover and roles /// may be unique to this codebase. abstract contract OwnableRoles is Ownable { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The `user`'s roles is updated to `roles`. /// Each bit of `roles` represents whether the role is set. event RolesUpdated(address indexed user, uint256 indexed roles); /// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`. uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE = 0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The role slot of `user` is given by: /// ``` /// mstore(0x00, or(shl(96, user), _ROLE_SLOT_SEED)) /// let roleSlot := keccak256(0x00, 0x20) /// ``` /// This automatically ignores the upper bits of the `user` in case /// they are not clean, as well as keep the `keccak256` under 32-bytes. /// /// Note: This is equivalent to `uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))`. uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Overwrite the roles directly without authorization guard. function _setRoles(address user, uint256 roles) internal virtual { /// @solidity memory-safe-assembly assembly { mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, user) // Store the new value. sstore(keccak256(0x0c, 0x20), roles) // Emit the {RolesUpdated} event. log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles) } } /// @dev Updates the roles directly without authorization guard. /// If `on` is true, each set bit of `roles` will be turned on, /// otherwise, each set bit of `roles` will be turned off. function _updateRoles(address user, uint256 roles, bool on) internal virtual { /// @solidity memory-safe-assembly assembly { mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, user) let roleSlot := keccak256(0x0c, 0x20) // Load the current value. let current := sload(roleSlot) // Compute the updated roles if `on` is true. let updated := or(current, roles) // Compute the updated roles if `on` is false. // Use `and` to compute the intersection of `current` and `roles`, // `xor` it with `current` to flip the bits in the intersection. if iszero(on) { updated := xor(current, and(current, roles)) } // Then, store the new value. sstore(roleSlot, updated) // Emit the {RolesUpdated} event. log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), updated) } } /// @dev Grants the roles directly without authorization guard. /// Each bit of `roles` represents the role to turn on. function _grantRoles(address user, uint256 roles) internal virtual { _updateRoles(user, roles, true); } /// @dev Removes the roles directly without authorization guard. /// Each bit of `roles` represents the role to turn off. function _removeRoles(address user, uint256 roles) internal virtual { _updateRoles(user, roles, false); } /// @dev Throws if the sender does not have any of the `roles`. function _checkRoles(uint256 roles) internal view virtual { /// @solidity memory-safe-assembly assembly { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, caller()) // Load the stored value, and if the `and` intersection // of the value and `roles` is zero, revert. if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) { mstore(0x00, 0x82b42900) // `Unauthorized()`. revert(0x1c, 0x04) } } } /// @dev Throws if the sender is not the owner, /// and does not have any of the `roles`. /// Checks for ownership first, then lazily checks for roles. function _checkOwnerOrRoles(uint256 roles) internal view virtual { /// @solidity memory-safe-assembly assembly { // If the caller is not the stored owner. // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`. if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, caller()) // Load the stored value, and if the `and` intersection // of the value and `roles` is zero, revert. if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) { mstore(0x00, 0x82b42900) // `Unauthorized()`. revert(0x1c, 0x04) } } } } /// @dev Throws if the sender does not have any of the `roles`, /// and is not the owner. /// Checks for roles first, then lazily checks for ownership. function _checkRolesOrOwner(uint256 roles) internal view virtual { /// @solidity memory-safe-assembly assembly { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, caller()) // Load the stored value, and if the `and` intersection // of the value and `roles` is zero, revert. if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) { // If the caller is not the stored owner. // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`. if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) { mstore(0x00, 0x82b42900) // `Unauthorized()`. revert(0x1c, 0x04) } } } } /// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`. /// This is meant for frontends like Etherscan, and is therefore not fully optimized. /// Not recommended to be called on-chain. /// Made internal to conserve bytecode. Wrap it in a public function if needed. function _rolesFromOrdinals(uint8[] memory ordinals) internal pure returns (uint256 roles) { /// @solidity memory-safe-assembly assembly { for { let i := shl(5, mload(ordinals)) } i { i := sub(i, 0x20) } { // We don't need to mask the values of `ordinals`, as Solidity // cleans dirty upper bits when storing variables into memory. roles := or(shl(mload(add(ordinals, i)), 1), roles) } } } /// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap. /// This is meant for frontends like Etherscan, and is therefore not fully optimized. /// Not recommended to be called on-chain. /// Made internal to conserve bytecode. Wrap it in a public function if needed. function _ordinalsFromRoles(uint256 roles) internal pure returns (uint8[] memory ordinals) { /// @solidity memory-safe-assembly assembly { // Grab the pointer to the free memory. ordinals := mload(0x40) let ptr := add(ordinals, 0x20) let o := 0 // The absence of lookup tables, De Bruijn, etc., here is intentional for // smaller bytecode, as this function is not meant to be called on-chain. for { let t := roles } 1 {} { mstore(ptr, o) // `shr` 5 is equivalent to multiplying by 0x20. // Push back into the ordinals array if the bit is set. ptr := add(ptr, shl(5, and(t, 1))) o := add(o, 1) t := shr(o, roles) if iszero(t) { break } } // Store the length of `ordinals`. mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20)))) // Allocate the memory. mstore(0x40, ptr) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC UPDATE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Allows the owner to grant `user` `roles`. /// If the `user` already has a role, then it will be an no-op for the role. function grantRoles(address user, uint256 roles) public payable virtual onlyOwner { _grantRoles(user, roles); } /// @dev Allows the owner to remove `user` `roles`. /// If the `user` does not have a role, then it will be an no-op for the role. function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner { _removeRoles(user, roles); } /// @dev Allow the caller to remove their own roles. /// If the caller does not have a role, then it will be an no-op for the role. function renounceRoles(uint256 roles) public payable virtual { _removeRoles(msg.sender, roles); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC READ FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the roles of `user`. function rolesOf(address user) public view virtual returns (uint256 roles) { /// @solidity memory-safe-assembly assembly { // Compute the role slot. mstore(0x0c, _ROLE_SLOT_SEED) mstore(0x00, user) // Load the stored value. roles := sload(keccak256(0x0c, 0x20)) } } /// @dev Returns whether `user` has any of `roles`. function hasAnyRole(address user, uint256 roles) public view virtual returns (bool) { return rolesOf(user) & roles != 0; } /// @dev Returns whether `user` has all of `roles`. function hasAllRoles(address user, uint256 roles) public view virtual returns (bool) { return rolesOf(user) & roles == roles; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MODIFIERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Marks a function as only callable by an account with `roles`. modifier onlyRoles(uint256 roles) virtual { _checkRoles(roles); _; } /// @dev Marks a function as only callable by the owner or by an account /// with `roles`. Checks for ownership first, then lazily checks for roles. modifier onlyOwnerOrRoles(uint256 roles) virtual { _checkOwnerOrRoles(roles); _; } /// @dev Marks a function as only callable by an account with `roles` /// or the owner. Checks for roles first, then lazily checks for ownership. modifier onlyRolesOrOwner(uint256 roles) virtual { _checkRolesOrOwner(roles); _; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ROLE CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // IYKYK uint256 internal constant _ROLE_0 = 1 << 0; uint256 internal constant _ROLE_1 = 1 << 1; uint256 internal constant _ROLE_2 = 1 << 2; uint256 internal constant _ROLE_3 = 1 << 3; uint256 internal constant _ROLE_4 = 1 << 4; uint256 internal constant _ROLE_5 = 1 << 5; uint256 internal constant _ROLE_6 = 1 << 6; uint256 internal constant _ROLE_7 = 1 << 7; uint256 internal constant _ROLE_8 = 1 << 8; uint256 internal constant _ROLE_9 = 1 << 9; uint256 internal constant _ROLE_10 = 1 << 10; uint256 internal constant _ROLE_11 = 1 << 11; uint256 internal constant _ROLE_12 = 1 << 12; uint256 internal constant _ROLE_13 = 1 << 13; uint256 internal constant _ROLE_14 = 1 << 14; uint256 internal constant _ROLE_15 = 1 << 15; uint256 internal constant _ROLE_16 = 1 << 16; uint256 internal constant _ROLE_17 = 1 << 17; uint256 internal constant _ROLE_18 = 1 << 18; uint256 internal constant _ROLE_19 = 1 << 19; uint256 internal constant _ROLE_20 = 1 << 20; uint256 internal constant _ROLE_21 = 1 << 21; uint256 internal constant _ROLE_22 = 1 << 22; uint256 internal constant _ROLE_23 = 1 << 23; uint256 internal constant _ROLE_24 = 1 << 24; uint256 internal constant _ROLE_25 = 1 << 25; uint256 internal constant _ROLE_26 = 1 << 26; uint256 internal constant _ROLE_27 = 1 << 27; uint256 internal constant _ROLE_28 = 1 << 28; uint256 internal constant _ROLE_29 = 1 << 29; uint256 internal constant _ROLE_30 = 1 << 30; uint256 internal constant _ROLE_31 = 1 << 31; uint256 internal constant _ROLE_32 = 1 << 32; uint256 internal constant _ROLE_33 = 1 << 33; uint256 internal constant _ROLE_34 = 1 << 34; uint256 internal constant _ROLE_35 = 1 << 35; uint256 internal constant _ROLE_36 = 1 << 36; uint256 internal constant _ROLE_37 = 1 << 37; uint256 internal constant _ROLE_38 = 1 << 38; uint256 internal constant _ROLE_39 = 1 << 39; uint256 internal constant _ROLE_40 = 1 << 40; uint256 internal constant _ROLE_41 = 1 << 41; uint256 internal constant _ROLE_42 = 1 << 42; uint256 internal constant _ROLE_43 = 1 << 43; uint256 internal constant _ROLE_44 = 1 << 44; uint256 internal constant _ROLE_45 = 1 << 45; uint256 internal constant _ROLE_46 = 1 << 46; uint256 internal constant _ROLE_47 = 1 << 47; uint256 internal constant _ROLE_48 = 1 << 48; uint256 internal constant _ROLE_49 = 1 << 49; uint256 internal constant _ROLE_50 = 1 << 50; uint256 internal constant _ROLE_51 = 1 << 51; uint256 internal constant _ROLE_52 = 1 << 52; uint256 internal constant _ROLE_53 = 1 << 53; uint256 internal constant _ROLE_54 = 1 << 54; uint256 internal constant _ROLE_55 = 1 << 55; uint256 internal constant _ROLE_56 = 1 << 56; uint256 internal constant _ROLE_57 = 1 << 57; uint256 internal constant _ROLE_58 = 1 << 58; uint256 internal constant _ROLE_59 = 1 << 59; uint256 internal constant _ROLE_60 = 1 << 60; uint256 internal constant _ROLE_61 = 1 << 61; uint256 internal constant _ROLE_62 = 1 << 62; uint256 internal constant _ROLE_63 = 1 << 63; uint256 internal constant _ROLE_64 = 1 << 64; uint256 internal constant _ROLE_65 = 1 << 65; uint256 internal constant _ROLE_66 = 1 << 66; uint256 internal constant _ROLE_67 = 1 << 67; uint256 internal constant _ROLE_68 = 1 << 68; uint256 internal constant _ROLE_69 = 1 << 69; uint256 internal constant _ROLE_70 = 1 << 70; uint256 internal constant _ROLE_71 = 1 << 71; uint256 internal constant _ROLE_72 = 1 << 72; uint256 internal constant _ROLE_73 = 1 << 73; uint256 internal constant _ROLE_74 = 1 << 74; uint256 internal constant _ROLE_75 = 1 << 75; uint256 internal constant _ROLE_76 = 1 << 76; uint256 internal constant _ROLE_77 = 1 << 77; uint256 internal constant _ROLE_78 = 1 << 78; uint256 internal constant _ROLE_79 = 1 << 79; uint256 internal constant _ROLE_80 = 1 << 80; uint256 internal constant _ROLE_81 = 1 << 81; uint256 internal constant _ROLE_82 = 1 << 82; uint256 internal constant _ROLE_83 = 1 << 83; uint256 internal constant _ROLE_84 = 1 << 84; uint256 internal constant _ROLE_85 = 1 << 85; uint256 internal constant _ROLE_86 = 1 << 86; uint256 internal constant _ROLE_87 = 1 << 87; uint256 internal constant _ROLE_88 = 1 << 88; uint256 internal constant _ROLE_89 = 1 << 89; uint256 internal constant _ROLE_90 = 1 << 90; uint256 internal constant _ROLE_91 = 1 << 91; uint256 internal constant _ROLE_92 = 1 << 92; uint256 internal constant _ROLE_93 = 1 << 93; uint256 internal constant _ROLE_94 = 1 << 94; uint256 internal constant _ROLE_95 = 1 << 95; uint256 internal constant _ROLE_96 = 1 << 96; uint256 internal constant _ROLE_97 = 1 << 97; uint256 internal constant _ROLE_98 = 1 << 98; uint256 internal constant _ROLE_99 = 1 << 99; uint256 internal constant _ROLE_100 = 1 << 100; uint256 internal constant _ROLE_101 = 1 << 101; uint256 internal constant _ROLE_102 = 1 << 102; uint256 internal constant _ROLE_103 = 1 << 103; uint256 internal constant _ROLE_104 = 1 << 104; uint256 internal constant _ROLE_105 = 1 << 105; uint256 internal constant _ROLE_106 = 1 << 106; uint256 internal constant _ROLE_107 = 1 << 107; uint256 internal constant _ROLE_108 = 1 << 108; uint256 internal constant _ROLE_109 = 1 << 109; uint256 internal constant _ROLE_110 = 1 << 110; uint256 internal constant _ROLE_111 = 1 << 111; uint256 internal constant _ROLE_112 = 1 << 112; uint256 internal constant _ROLE_113 = 1 << 113; uint256 internal constant _ROLE_114 = 1 << 114; uint256 internal constant _ROLE_115 = 1 << 115; uint256 internal constant _ROLE_116 = 1 << 116; uint256 internal constant _ROLE_117 = 1 << 117; uint256 internal constant _ROLE_118 = 1 << 118; uint256 internal constant _ROLE_119 = 1 << 119; uint256 internal constant _ROLE_120 = 1 << 120; uint256 internal constant _ROLE_121 = 1 << 121; uint256 internal constant _ROLE_122 = 1 << 122; uint256 internal constant _ROLE_123 = 1 << 123; uint256 internal constant _ROLE_124 = 1 << 124; uint256 internal constant _ROLE_125 = 1 << 125; uint256 internal constant _ROLE_126 = 1 << 126; uint256 internal constant _ROLE_127 = 1 << 127; uint256 internal constant _ROLE_128 = 1 << 128; uint256 internal constant _ROLE_129 = 1 << 129; uint256 internal constant _ROLE_130 = 1 << 130; uint256 internal constant _ROLE_131 = 1 << 131; uint256 internal constant _ROLE_132 = 1 << 132; uint256 internal constant _ROLE_133 = 1 << 133; uint256 internal constant _ROLE_134 = 1 << 134; uint256 internal constant _ROLE_135 = 1 << 135; uint256 internal constant _ROLE_136 = 1 << 136; uint256 internal constant _ROLE_137 = 1 << 137; uint256 internal constant _ROLE_138 = 1 << 138; uint256 internal constant _ROLE_139 = 1 << 139; uint256 internal constant _ROLE_140 = 1 << 140; uint256 internal constant _ROLE_141 = 1 << 141; uint256 internal constant _ROLE_142 = 1 << 142; uint256 internal constant _ROLE_143 = 1 << 143; uint256 internal constant _ROLE_144 = 1 << 144; uint256 internal constant _ROLE_145 = 1 << 145; uint256 internal constant _ROLE_146 = 1 << 146; uint256 internal constant _ROLE_147 = 1 << 147; uint256 internal constant _ROLE_148 = 1 << 148; uint256 internal constant _ROLE_149 = 1 << 149; uint256 internal constant _ROLE_150 = 1 << 150; uint256 internal constant _ROLE_151 = 1 << 151; uint256 internal constant _ROLE_152 = 1 << 152; uint256 internal constant _ROLE_153 = 1 << 153; uint256 internal constant _ROLE_154 = 1 << 154; uint256 internal constant _ROLE_155 = 1 << 155; uint256 internal constant _ROLE_156 = 1 << 156; uint256 internal constant _ROLE_157 = 1 << 157; uint256 internal constant _ROLE_158 = 1 << 158; uint256 internal constant _ROLE_159 = 1 << 159; uint256 internal constant _ROLE_160 = 1 << 160; uint256 internal constant _ROLE_161 = 1 << 161; uint256 internal constant _ROLE_162 = 1 << 162; uint256 internal constant _ROLE_163 = 1 << 163; uint256 internal constant _ROLE_164 = 1 << 164; uint256 internal constant _ROLE_165 = 1 << 165; uint256 internal constant _ROLE_166 = 1 << 166; uint256 internal constant _ROLE_167 = 1 << 167; uint256 internal constant _ROLE_168 = 1 << 168; uint256 internal constant _ROLE_169 = 1 << 169; uint256 internal constant _ROLE_170 = 1 << 170; uint256 internal constant _ROLE_171 = 1 << 171; uint256 internal constant _ROLE_172 = 1 << 172; uint256 internal constant _ROLE_173 = 1 << 173; uint256 internal constant _ROLE_174 = 1 << 174; uint256 internal constant _ROLE_175 = 1 << 175; uint256 internal constant _ROLE_176 = 1 << 176; uint256 internal constant _ROLE_177 = 1 << 177; uint256 internal constant _ROLE_178 = 1 << 178; uint256 internal constant _ROLE_179 = 1 << 179; uint256 internal constant _ROLE_180 = 1 << 180; uint256 internal constant _ROLE_181 = 1 << 181; uint256 internal constant _ROLE_182 = 1 << 182; uint256 internal constant _ROLE_183 = 1 << 183; uint256 internal constant _ROLE_184 = 1 << 184; uint256 internal constant _ROLE_185 = 1 << 185; uint256 internal constant _ROLE_186 = 1 << 186; uint256 internal constant _ROLE_187 = 1 << 187; uint256 internal constant _ROLE_188 = 1 << 188; uint256 internal constant _ROLE_189 = 1 << 189; uint256 internal constant _ROLE_190 = 1 << 190; uint256 internal constant _ROLE_191 = 1 << 191; uint256 internal constant _ROLE_192 = 1 << 192; uint256 internal constant _ROLE_193 = 1 << 193; uint256 internal constant _ROLE_194 = 1 << 194; uint256 internal constant _ROLE_195 = 1 << 195; uint256 internal constant _ROLE_196 = 1 << 196; uint256 internal constant _ROLE_197 = 1 << 197; uint256 internal constant _ROLE_198 = 1 << 198; uint256 internal constant _ROLE_199 = 1 << 199; uint256 internal constant _ROLE_200 = 1 << 200; uint256 internal constant _ROLE_201 = 1 << 201; uint256 internal constant _ROLE_202 = 1 << 202; uint256 internal constant _ROLE_203 = 1 << 203; uint256 internal constant _ROLE_204 = 1 << 204; uint256 internal constant _ROLE_205 = 1 << 205; uint256 internal constant _ROLE_206 = 1 << 206; uint256 internal constant _ROLE_207 = 1 << 207; uint256 internal constant _ROLE_208 = 1 << 208; uint256 internal constant _ROLE_209 = 1 << 209; uint256 internal constant _ROLE_210 = 1 << 210; uint256 internal constant _ROLE_211 = 1 << 211; uint256 internal constant _ROLE_212 = 1 << 212; uint256 internal constant _ROLE_213 = 1 << 213; uint256 internal constant _ROLE_214 = 1 << 214; uint256 internal constant _ROLE_215 = 1 << 215; uint256 internal constant _ROLE_216 = 1 << 216; uint256 internal constant _ROLE_217 = 1 << 217; uint256 internal constant _ROLE_218 = 1 << 218; uint256 internal constant _ROLE_219 = 1 << 219; uint256 internal constant _ROLE_220 = 1 << 220; uint256 internal constant _ROLE_221 = 1 << 221; uint256 internal constant _ROLE_222 = 1 << 222; uint256 internal constant _ROLE_223 = 1 << 223; uint256 internal constant _ROLE_224 = 1 << 224; uint256 internal constant _ROLE_225 = 1 << 225; uint256 internal constant _ROLE_226 = 1 << 226; uint256 internal constant _ROLE_227 = 1 << 227; uint256 internal constant _ROLE_228 = 1 << 228; uint256 internal constant _ROLE_229 = 1 << 229; uint256 internal constant _ROLE_230 = 1 << 230; uint256 internal constant _ROLE_231 = 1 << 231; uint256 internal constant _ROLE_232 = 1 << 232; uint256 internal constant _ROLE_233 = 1 << 233; uint256 internal constant _ROLE_234 = 1 << 234; uint256 internal constant _ROLE_235 = 1 << 235; uint256 internal constant _ROLE_236 = 1 << 236; uint256 internal constant _ROLE_237 = 1 << 237; uint256 internal constant _ROLE_238 = 1 << 238; uint256 internal constant _ROLE_239 = 1 << 239; uint256 internal constant _ROLE_240 = 1 << 240; uint256 internal constant _ROLE_241 = 1 << 241; uint256 internal constant _ROLE_242 = 1 << 242; uint256 internal constant _ROLE_243 = 1 << 243; uint256 internal constant _ROLE_244 = 1 << 244; uint256 internal constant _ROLE_245 = 1 << 245; uint256 internal constant _ROLE_246 = 1 << 246; uint256 internal constant _ROLE_247 = 1 << 247; uint256 internal constant _ROLE_248 = 1 << 248; uint256 internal constant _ROLE_249 = 1 << 249; uint256 internal constant _ROLE_250 = 1 << 250; uint256 internal constant _ROLE_251 = 1 << 251; uint256 internal constant _ROLE_252 = 1 << 252; uint256 internal constant _ROLE_253 = 1 << 253; uint256 internal constant _ROLE_254 = 1 << 254; uint256 internal constant _ROLE_255 = 1 << 255; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library for interacting with ERC6551 accounts. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/accounts/LibERC6551.sol) /// @author ERC6551 team (https://github.com/erc6551/reference/blob/main/src/lib/ERC6551AccountLib.sol) library LibERC6551 { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Failed to create a ERC6551 account via the registry. error AccountCreationFailed(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The canonical ERC6551 registry address for EVM chains. address internal constant REGISTRY = 0x000000006551c19487814612e58FE06813775758; /// @dev The canonical ERC6551 registry bytecode for EVM chains. /// Useful for forge tests: /// `vm.etch(REGISTRY, REGISTRY_BYTECODE)`. bytes internal constant REGISTRY_BYTECODE = hex"608060405234801561001057600080fd5b50600436106100365760003560e01c8063246a00211461003b5780638a54c52f1461006a575b600080fd5b61004e6100493660046101b7565b61007d565b6040516001600160a01b03909116815260200160405180910390f35b61004e6100783660046101b7565b6100e1565b600060806024608c376e5af43d82803e903d91602b57fd5bf3606c5285605d52733d60ad80600a3d3981f3363d3d373d3d3d363d7360495260ff60005360b76055206035523060601b60015284601552605560002060601b60601c60005260206000f35b600060806024608c376e5af43d82803e903d91602b57fd5bf3606c5285605d52733d60ad80600a3d3981f3363d3d373d3d3d363d7360495260ff60005360b76055206035523060601b600152846015526055600020803b61018b578560b760556000f580610157576320188a596000526004601cfd5b80606c52508284887f79f19b3655ee38b1ce526556b7731a20c8f218fbda4a3990b6cc4172fdf887226060606ca46020606cf35b8060601b60601c60005260206000f35b80356001600160a01b03811681146101b257600080fd5b919050565b600080600080600060a086880312156101cf57600080fd5b6101d88661019b565b945060208601359350604086013592506101f46060870161019b565b94979396509194608001359291505056fea2646970667358221220ea2fe53af507453c64dd7c1db05549fa47a298dfb825d6d11e1689856135f16764736f6c63430008110033"; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ACCOUNT BYTECODE OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the initialization code of the ERC6551 account. function initCode( address implementation_, bytes32 salt_, uint256 chainId_, address tokenContract_, uint256 tokenId_ ) internal pure returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) // Grab the free memory pointer.. // Layout the variables and bytecode backwards. mstore(add(result, 0xb7), tokenId_) mstore(add(result, 0x97), shr(96, shl(96, tokenContract_))) mstore(add(result, 0x77), chainId_) mstore(add(result, 0x57), salt_) mstore(add(result, 0x37), 0x5af43d82803e903d91602b57fd5bf3) mstore(add(result, 0x28), implementation_) mstore(add(result, 0x14), 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) mstore(result, 0xb7) // Store the length. mstore(0x40, add(result, 0xd7)) // Allocate the memory. } } /// @dev Returns the initialization code hash of the ERC6551 account. function initCodeHash( address implementation_, bytes32 salt_, uint256 chainId_, address tokenContract_, uint256 tokenId_ ) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) // Grab the free memory pointer. // Layout the variables and bytecode backwards. mstore(add(result, 0xa3), tokenId_) mstore(add(result, 0x83), shr(96, shl(96, tokenContract_))) mstore(add(result, 0x63), chainId_) mstore(add(result, 0x43), salt_) mstore(add(result, 0x23), 0x5af43d82803e903d91602b57fd5bf3) mstore(add(result, 0x14), implementation_) mstore(result, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) result := keccak256(add(result, 0x0c), 0xb7) } } /// @dev Creates an account via the ERC6551 registry. function createAccount( address implementation_, bytes32 salt_, uint256 chainId_, address tokenContract_, uint256 tokenId_ ) internal returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) mstore(add(m, 0x14), implementation_) mstore(add(m, 0x34), salt_) mstore(add(m, 0x54), chainId_) mstore(add(m, 0x74), shr(96, shl(96, tokenContract_))) mstore(add(m, 0x94), tokenId_) // `createAccount(address,bytes32,uint256,address,uint256)`. mstore(m, 0x8a54c52f000000000000000000000000) if iszero( and( gt(returndatasize(), 0x1f), call(gas(), REGISTRY, 0, add(m, 0x10), 0xa4, 0x00, 0x20) ) ) { mstore(0x00, 0x20188a59) // `AccountCreationFailed()`. revert(0x1c, 0x04) } result := mload(0x00) } } /// @dev Returns the address of the ERC6551 account. function account( address implementation_, bytes32 salt_, uint256 chainId_, address tokenContract_, uint256 tokenId_ ) internal pure returns (address result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) // Grab the free memory pointer. // Layout the variables and bytecode backwards. mstore(add(result, 0xa3), tokenId_) mstore(add(result, 0x83), shr(96, shl(96, tokenContract_))) mstore(add(result, 0x63), chainId_) mstore(add(result, 0x43), salt_) mstore(add(result, 0x23), 0x5af43d82803e903d91602b57fd5bf3) mstore(add(result, 0x14), implementation_) mstore(result, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) // Compute and store the bytecode hash. mstore8(0x00, 0xff) // Write the prefix. mstore(0x35, keccak256(add(result, 0x0c), 0xb7)) mstore(0x01, shl(96, REGISTRY)) mstore(0x15, salt_) result := keccak256(0x00, 0x55) mstore(0x35, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Returns if `a` is an ERC6551 account with `expectedImplementation`. function isERC6551Account(address a, address expectedImplementation) internal view returns (bool result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Grab the free memory pointer.. extcodecopy(a, add(m, 0x20), 0x0a, 0xa3) let implementation_ := shr(96, mload(add(m, 0x20))) if mul( extcodesize(implementation_), gt(eq(extcodesize(a), 0xad), shl(96, xor(expectedImplementation, implementation_))) ) { // Layout the variables and bytecode backwards. mstore(add(m, 0x23), 0x5af43d82803e903d91602b57fd5bf3) mstore(add(m, 0x14), implementation_) mstore(m, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) // Compute and store the bytecode hash. mstore8(0x00, 0xff) // Write the prefix. mstore(0x35, keccak256(add(m, 0x0c), 0xb7)) mstore(0x01, shl(96, REGISTRY)) mstore(0x15, mload(add(m, 0x43))) result := iszero(shl(96, xor(a, keccak256(0x00, 0x55)))) mstore(0x35, 0) // Restore the overwritten part of the free memory pointer. } } } /// @dev Returns the implementation of the ERC6551 account `a`. function implementation(address a) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { extcodecopy(a, 0x00, 0x0a, 0x14) result := shr(96, mload(0x00)) } } /// @dev Returns the static variables of the ERC6551 account `a`. function context(address a) internal view returns (bytes32 salt_, uint256 chainId_, address tokenContract_, uint256 tokenId_) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. extcodecopy(a, 0x00, 0x2d, 0x80) salt_ := mload(0x00) chainId_ := mload(0x20) tokenContract_ := mload(0x40) tokenId_ := mload(0x60) mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero pointer. } } /// @dev Returns the salt of the ERC6551 account `a`. function salt(address a) internal view returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { extcodecopy(a, 0x00, 0x2d, 0x20) result := mload(0x00) } } /// @dev Returns the chain ID of the ERC6551 account `a`. function chainId(address a) internal view returns (uint256 result) { /// @solidity memory-safe-assembly assembly { extcodecopy(a, 0x00, 0x4d, 0x20) result := mload(0x00) } } /// @dev Returns the token contract of the ERC6551 account `a`. function tokenContract(address a) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { extcodecopy(a, 0x00, 0x6d, 0x20) result := mload(0x00) } } /// @dev Returns the token ID of the ERC6551 account `a`. function tokenId(address a) internal view returns (uint256 result) { /// @solidity memory-safe-assembly assembly { extcodecopy(a, 0x00, 0x8d, 0x20) result := mload(0x00) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library for converting numbers into strings and other string operations. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol) /// /// @dev Note: /// For performance and bytecode compactness, most of the string operations are restricted to /// byte strings (7-bit ASCII), except where otherwise specified. /// Usage of byte string operations on charsets with runes spanning two or more bytes /// can lead to undefined behavior. library LibString { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The length of the output is too small to contain all the hex digits. error HexLengthInsufficient(); /// @dev The length of the string is more than 32 bytes. error TooBigForSmallString(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The constant returned when the `search` is not found in the string. uint256 internal constant NOT_FOUND = type(uint256).max; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* DECIMAL OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the base 10 decimal representation of `value`. function toString(uint256 value) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { // The maximum value of a uint256 contains 78 digits (1 byte per digit), but // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned. // We will need 1 word for the trailing zeros padding, 1 word for the length, // and 3 words for a maximum of 78 digits. str := add(mload(0x40), 0x80) // Update the free memory pointer to allocate. mstore(0x40, add(str, 0x20)) // Zeroize the slot after the string. mstore(str, 0) // Cache the end of the memory to calculate the length later. let end := str let w := not(0) // Tsk. // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let temp := value } 1 {} { str := add(str, w) // `sub(str, 1)`. // Write the character to the pointer. // The ASCII index of the '0' character is 48. mstore8(str, add(48, mod(temp, 10))) // Keep dividing `temp` until zero. temp := div(temp, 10) if iszero(temp) { break } } let length := sub(end, str) // Move the pointer 32 bytes leftwards to make room for the length. str := sub(str, 0x20) // Store the length. mstore(str, length) } } /// @dev Returns the base 10 decimal representation of `value`. function toString(int256 value) internal pure returns (string memory str) { if (value >= 0) { return toString(uint256(value)); } unchecked { str = toString(~uint256(value) + 1); } /// @solidity memory-safe-assembly assembly { // We still have some spare memory space on the left, // as we have allocated 3 words (96 bytes) for up to 78 digits. let length := mload(str) // Load the string length. mstore(str, 0x2d) // Store the '-' character. str := sub(str, 1) // Move back the string pointer by a byte. mstore(str, add(length, 1)) // Update the string length. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HEXADECIMAL OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the hexadecimal representation of `value`, /// left-padded to an input length of `length` bytes. /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte, /// giving a total length of `length * 2 + 2` bytes. /// Reverts if `length` is too small for the output to contain all the digits. function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) { str = toHexStringNoPrefix(value, length); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hexadecimal representation of `value`, /// left-padded to an input length of `length` bytes. /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte, /// giving a total length of `length * 2` bytes. /// Reverts if `length` is too small for the output to contain all the digits. function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length. // We add 0x20 to the total and round down to a multiple of 0x20. // (0x20 + 0x20 + 0x02 + 0x20) = 0x62. str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f))) // Allocate the memory. mstore(0x40, add(str, 0x20)) // Zeroize the slot after the string. mstore(str, 0) // Cache the end to calculate the length later. let end := str // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let start := sub(str, add(length, length)) let w := not(1) // Tsk. let temp := value // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for {} 1 {} { str := add(str, w) // `sub(str, 2)`. mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) if iszero(xor(str, start)) { break } } if temp { mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`. revert(0x1c, 0x04) } // Compute the string's length. let strLength := sub(end, str) // Move the pointer and write the length. str := sub(str, 0x20) mstore(str, strLength) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. /// As address are 20 bytes long, the output will left-padded to have /// a length of `20 * 2 + 2` bytes. function toHexString(uint256 value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x". /// The output excludes leading "0" from the `toHexString` output. /// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`. function toMinimalHexString(uint256 value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present. let strLength := add(mload(str), 2) // Compute the length. mstore(add(str, o), 0x3078) // Write the "0x" prefix, accounting for leading zero. str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero. mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero. } } /// @dev Returns the hexadecimal representation of `value`. /// The output excludes leading "0" from the `toHexStringNoPrefix` output. /// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`. function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present. let strLength := mload(str) // Get the length. str := add(str, o) // Move the pointer, accounting for leading zero. mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is encoded using 2 hexadecimal digits per byte. /// As address are 20 bytes long, the output will left-padded to have /// a length of `20 * 2` bytes. function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, // 0x02 bytes for the prefix, and 0x40 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0. str := add(mload(0x40), 0x80) // Allocate the memory. mstore(0x40, add(str, 0x20)) // Zeroize the slot after the string. mstore(str, 0) // Cache the end to calculate the length later. let end := str // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let w := not(1) // Tsk. // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let temp := value } 1 {} { str := add(str, w) // `sub(str, 2)`. mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) if iszero(temp) { break } } // Compute the string's length. let strLength := sub(end, str) // Move the pointer and write the length. str := sub(str, 0x20) mstore(str, strLength) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte, /// and the alphabets are capitalized conditionally according to /// https://eips.ethereum.org/EIPS/eip-55 function toHexStringChecksummed(address value) internal pure returns (string memory str) { str = toHexString(value); /// @solidity memory-safe-assembly assembly { let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...` let o := add(str, 0x22) let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... ` let t := shl(240, 136) // `0b10001000 << 240` for { let i := 0 } 1 {} { mstore(add(i, i), mul(t, byte(i, hashed))) i := add(i, 1) if eq(i, 20) { break } } mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask))))) o := add(o, 0x20) mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask))))) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. function toHexString(address value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(address value) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { str := mload(0x40) // Allocate the memory. // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, // 0x02 bytes for the prefix, and 0x28 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80. mstore(0x40, add(str, 0x80)) // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) str := add(str, 2) mstore(str, 40) let o := add(str, 0x20) mstore(add(o, 40), 0) value := shl(96, value) // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let i := 0 } 1 {} { let p := add(o, add(i, i)) let temp := byte(i, value) mstore8(add(p, 1), mload(and(temp, 15))) mstore8(p, mload(shr(4, temp))) i := add(i, 1) if eq(i, 20) { break } } } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexString(bytes memory raw) internal pure returns (string memory str) { str = toHexStringNoPrefix(raw); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { let length := mload(raw) str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix. mstore(str, add(length, length)) // Store the length of the output. // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let o := add(str, 0x20) let end := add(raw, length) for {} iszero(eq(raw, end)) {} { raw := add(raw, 1) mstore8(add(o, 1), mload(and(mload(raw), 15))) mstore8(o, mload(and(shr(4, mload(raw)), 15))) o := add(o, 2) } mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate the memory. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* RUNE STRING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the number of UTF characters in the string. function runeCount(string memory s) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { if mload(s) { mstore(0x00, div(not(0), 255)) mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506) let o := add(s, 0x20) let end := add(o, mload(s)) for { result := 1 } 1 { result := add(result, 1) } { o := add(o, byte(0, mload(shr(250, mload(o))))) if iszero(lt(o, end)) { break } } } } } /// @dev Returns if this string is a 7-bit ASCII string. /// (i.e. all characters codes are in [0..127]) function is7BitASCII(string memory s) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let mask := shl(7, div(not(0), 255)) result := 1 let n := mload(s) if n { let o := add(s, 0x20) let end := add(o, n) let last := mload(end) mstore(end, 0) for {} 1 {} { if and(mask, mload(o)) { result := 0 break } o := add(o, 0x20) if iszero(lt(o, end)) { break } } mstore(end, last) } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* BYTE STRING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // For performance and bytecode compactness, byte string operations are restricted // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets. // Usage of byte string operations on charsets with runes spanning two or more bytes // can lead to undefined behavior. /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`. function replace(string memory subject, string memory search, string memory replacement) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) let searchLength := mload(search) let replacementLength := mload(replacement) subject := add(subject, 0x20) search := add(search, 0x20) replacement := add(replacement, 0x20) result := add(mload(0x40), 0x20) let subjectEnd := add(subject, subjectLength) if iszero(gt(searchLength, subjectLength)) { let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1) let h := 0 if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) } let m := shl(3, sub(0x20, and(searchLength, 0x1f))) let s := mload(search) for {} 1 {} { let t := mload(subject) // Whether the first `searchLength % 32` bytes of // `subject` and `search` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(subject, searchLength), h)) { mstore(result, t) result := add(result, 1) subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } continue } } // Copy the `replacement` one word at a time. for { let o := 0 } 1 {} { mstore(add(result, o), mload(add(replacement, o))) o := add(o, 0x20) if iszero(lt(o, replacementLength)) { break } } result := add(result, replacementLength) subject := add(subject, searchLength) if searchLength { if iszero(lt(subject, subjectSearchEnd)) { break } continue } } mstore(result, t) result := add(result, 1) subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } } } let resultRemainder := result result := add(mload(0x40), 0x20) let k := add(sub(resultRemainder, result), sub(subjectEnd, subject)) // Copy the rest of the string one word at a time. for {} lt(subject, subjectEnd) {} { mstore(resultRemainder, mload(subject)) resultRemainder := add(resultRemainder, 0x20) subject := add(subject, 0x20) } result := sub(result, 0x20) let last := add(add(result, 0x20), k) // Zeroize the slot after the string. mstore(last, 0) mstore(0x40, add(last, 0x20)) // Allocate the memory. mstore(result, k) // Store the length. } } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from left to right, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function indexOf(string memory subject, string memory search, uint256 from) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for { let subjectLength := mload(subject) } 1 {} { if iszero(mload(search)) { if iszero(gt(from, subjectLength)) { result := from break } result := subjectLength break } let searchLength := mload(search) let subjectStart := add(subject, 0x20) result := not(0) // Initialize to `NOT_FOUND`. subject := add(subjectStart, from) let end := add(sub(add(subjectStart, subjectLength), searchLength), 1) let m := shl(3, sub(0x20, and(searchLength, 0x1f))) let s := mload(add(search, 0x20)) if iszero(and(lt(subject, end), lt(from, subjectLength))) { break } if iszero(lt(searchLength, 0x20)) { for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} { if iszero(shr(m, xor(mload(subject), s))) { if eq(keccak256(subject, searchLength), h) { result := sub(subject, subjectStart) break } } subject := add(subject, 1) if iszero(lt(subject, end)) { break } } break } for {} 1 {} { if iszero(shr(m, xor(mload(subject), s))) { result := sub(subject, subjectStart) break } subject := add(subject, 1) if iszero(lt(subject, end)) { break } } break } } } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from left to right. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function indexOf(string memory subject, string memory search) internal pure returns (uint256 result) { result = indexOf(subject, search, 0); } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from right to left, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function lastIndexOf(string memory subject, string memory search, uint256 from) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for {} 1 {} { result := not(0) // Initialize to `NOT_FOUND`. let searchLength := mload(search) if gt(searchLength, mload(subject)) { break } let w := result let fromMax := sub(mload(subject), searchLength) if iszero(gt(fromMax, from)) { from := fromMax } let end := add(add(subject, 0x20), w) subject := add(add(subject, 0x20), from) if iszero(gt(subject, end)) { break } // As this function is not too often used, // we shall simply use keccak256 for smaller bytecode size. for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} { if eq(keccak256(subject, searchLength), h) { result := sub(subject, add(end, 1)) break } subject := add(subject, w) // `sub(subject, 1)`. if iszero(gt(subject, end)) { break } } break } } } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from right to left. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function lastIndexOf(string memory subject, string memory search) internal pure returns (uint256 result) { result = lastIndexOf(subject, search, uint256(int256(-1))); } /// @dev Returns true if `search` is found in `subject`, false otherwise. function contains(string memory subject, string memory search) internal pure returns (bool) { return indexOf(subject, search) != NOT_FOUND; } /// @dev Returns whether `subject` starts with `search`. function startsWith(string memory subject, string memory search) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let searchLength := mload(search) // Just using keccak256 directly is actually cheaper. // forgefmt: disable-next-item result := and( iszero(gt(searchLength, mload(subject))), eq( keccak256(add(subject, 0x20), searchLength), keccak256(add(search, 0x20), searchLength) ) ) } } /// @dev Returns whether `subject` ends with `search`. function endsWith(string memory subject, string memory search) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let searchLength := mload(search) let subjectLength := mload(subject) // Whether `search` is not longer than `subject`. let withinRange := iszero(gt(searchLength, subjectLength)) // Just using keccak256 directly is actually cheaper. // forgefmt: disable-next-item result := and( withinRange, eq( keccak256( // `subject + 0x20 + max(subjectLength - searchLength, 0)`. add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))), searchLength ), keccak256(add(search, 0x20), searchLength) ) ) } } /// @dev Returns `subject` repeated `times`. function repeat(string memory subject, uint256 times) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) if iszero(or(iszero(times), iszero(subjectLength))) { subject := add(subject, 0x20) result := mload(0x40) let output := add(result, 0x20) for {} 1 {} { // Copy the `subject` one word at a time. for { let o := 0 } 1 {} { mstore(add(output, o), mload(add(subject, o))) o := add(o, 0x20) if iszero(lt(o, subjectLength)) { break } } output := add(output, subjectLength) times := sub(times, 1) if iszero(times) { break } } mstore(output, 0) // Zeroize the slot after the string. let resultLength := sub(output, add(result, 0x20)) mstore(result, resultLength) // Store the length. // Allocate the memory. mstore(0x40, add(result, add(resultLength, 0x20))) } } } /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive). /// `start` and `end` are byte offsets. function slice(string memory subject, uint256 start, uint256 end) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) if iszero(gt(subjectLength, end)) { end := subjectLength } if iszero(gt(subjectLength, start)) { start := subjectLength } if lt(start, end) { result := mload(0x40) let resultLength := sub(end, start) mstore(result, resultLength) subject := add(subject, start) let w := not(0x1f) // Copy the `subject` one word at a time, backwards. for { let o := and(add(resultLength, 0x1f), w) } 1 {} { mstore(add(result, o), mload(add(subject, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } // Zeroize the slot after the string. mstore(add(add(result, 0x20), resultLength), 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, add(result, and(add(resultLength, 0x3f), w))) } } } /// @dev Returns a copy of `subject` sliced from `start` to the end of the string. /// `start` is a byte offset. function slice(string memory subject, uint256 start) internal pure returns (string memory result) { result = slice(subject, start, uint256(int256(-1))); } /// @dev Returns all the indices of `search` in `subject`. /// The indices are byte offsets. function indicesOf(string memory subject, string memory search) internal pure returns (uint256[] memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) let searchLength := mload(search) if iszero(gt(searchLength, subjectLength)) { subject := add(subject, 0x20) search := add(search, 0x20) result := add(mload(0x40), 0x20) let subjectStart := subject let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1) let h := 0 if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) } let m := shl(3, sub(0x20, and(searchLength, 0x1f))) let s := mload(search) for {} 1 {} { let t := mload(subject) // Whether the first `searchLength % 32` bytes of // `subject` and `search` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(subject, searchLength), h)) { subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } continue } } // Append to `result`. mstore(result, sub(subject, subjectStart)) result := add(result, 0x20) // Advance `subject` by `searchLength`. subject := add(subject, searchLength) if searchLength { if iszero(lt(subject, subjectSearchEnd)) { break } continue } } subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } } let resultEnd := result // Assign `result` to the free memory pointer. result := mload(0x40) // Store the length of `result`. mstore(result, shr(5, sub(resultEnd, add(result, 0x20)))) // Allocate memory for result. // We allocate one more word, so this array can be recycled for {split}. mstore(0x40, add(resultEnd, 0x20)) } } } /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string. function split(string memory subject, string memory delimiter) internal pure returns (string[] memory result) { uint256[] memory indices = indicesOf(subject, delimiter); /// @solidity memory-safe-assembly assembly { let w := not(0x1f) let indexPtr := add(indices, 0x20) let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1))) mstore(add(indicesEnd, w), mload(subject)) mstore(indices, add(mload(indices), 1)) let prevIndex := 0 for {} 1 {} { let index := mload(indexPtr) mstore(indexPtr, 0x60) if iszero(eq(index, prevIndex)) { let element := mload(0x40) let elementLength := sub(index, prevIndex) mstore(element, elementLength) // Copy the `subject` one word at a time, backwards. for { let o := and(add(elementLength, 0x1f), w) } 1 {} { mstore(add(element, o), mload(add(add(subject, prevIndex), o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } // Zeroize the slot after the string. mstore(add(add(element, 0x20), elementLength), 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, add(element, and(add(elementLength, 0x3f), w))) // Store the `element` into the array. mstore(indexPtr, element) } prevIndex := add(index, mload(delimiter)) indexPtr := add(indexPtr, 0x20) if iszero(lt(indexPtr, indicesEnd)) { break } } result := indices if iszero(mload(delimiter)) { result := add(indices, 0x20) mstore(result, sub(mload(indices), 2)) } } } /// @dev Returns a concatenated string of `a` and `b`. /// Cheaper than `string.concat()` and does not de-align the free memory pointer. function concat(string memory a, string memory b) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let w := not(0x1f) result := mload(0x40) let aLength := mload(a) // Copy `a` one word at a time, backwards. for { let o := and(add(aLength, 0x20), w) } 1 {} { mstore(add(result, o), mload(add(a, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } let bLength := mload(b) let output := add(result, aLength) // Copy `b` one word at a time, backwards. for { let o := and(add(bLength, 0x20), w) } 1 {} { mstore(add(output, o), mload(add(b, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } let totalLength := add(aLength, bLength) let last := add(add(result, 0x20), totalLength) // Zeroize the slot after the string. mstore(last, 0) // Stores the length. mstore(result, totalLength) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, and(add(last, 0x1f), w)) } } /// @dev Returns a copy of the string in either lowercase or UPPERCASE. /// WARNING! This function is only compatible with 7-bit ASCII strings. function toCase(string memory subject, bool toUpper) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let length := mload(subject) if length { result := add(mload(0x40), 0x20) subject := add(subject, 1) let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff) let w := not(0) for { let o := length } 1 {} { o := add(o, w) let b := and(0xff, mload(add(subject, o))) mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20))) if iszero(o) { break } } result := mload(0x40) mstore(result, length) // Store the length. let last := add(add(result, 0x20), length) mstore(last, 0) // Zeroize the slot after the string. mstore(0x40, add(last, 0x20)) // Allocate the memory. } } } /// @dev Returns a string from a small bytes32 string. /// `s` must be null-terminated, or behavior will be undefined. function fromSmallString(bytes32 s) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let n := 0 for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'. mstore(result, n) let o := add(result, 0x20) mstore(o, s) mstore(add(o, n), 0) mstore(0x40, add(result, 0x40)) } } /// @dev Returns the small string, with all bytes after the first null byte zeroized. function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'. mstore(0x00, s) mstore(result, 0x00) result := mload(0x00) } } /// @dev Returns the string as a normalized null-terminated small string. function toSmallString(string memory s) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { result := mload(s) if iszero(lt(result, 33)) { mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`. revert(0x1c, 0x04) } result := shl(shl(3, sub(32, result)), mload(add(s, result))) } } /// @dev Returns a lowercased copy of the string. /// WARNING! This function is only compatible with 7-bit ASCII strings. function lower(string memory subject) internal pure returns (string memory result) { result = toCase(subject, false); } /// @dev Returns an UPPERCASED copy of the string. /// WARNING! This function is only compatible with 7-bit ASCII strings. function upper(string memory subject) internal pure returns (string memory result) { result = toCase(subject, true); } /// @dev Escapes the string to be used within HTML tags. function escapeHTML(string memory s) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let end := add(s, mload(s)) result := add(mload(0x40), 0x20) // Store the bytes of the packed offsets and strides into the scratch space. // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6. mstore(0x1f, 0x900094) mstore(0x08, 0xc0000000a6ab) // Store ""&'<>" into the scratch space. mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b)) for {} iszero(eq(s, end)) {} { s := add(s, 1) let c := and(mload(s), 0xff) // Not in `["\"","'","&","<",">"]`. if iszero(and(shl(c, 1), 0x500000c400000000)) { mstore8(result, c) result := add(result, 1) continue } let t := shr(248, mload(c)) mstore(result, mload(and(t, 0x1f))) result := add(result, shr(5, t)) } let last := result mstore(last, 0) // Zeroize the slot after the string. result := mload(0x40) mstore(result, sub(last, add(result, 0x20))) // Store the length. mstore(0x40, add(last, 0x20)) // Allocate the memory. } } /// @dev Escapes the string to be used within double-quotes in a JSON. /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes. function escapeJSON(string memory s, bool addDoubleQuotes) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let end := add(s, mload(s)) result := add(mload(0x40), 0x20) if addDoubleQuotes { mstore8(result, 34) result := add(1, result) } // Store "\\u0000" in scratch space. // Store "0123456789abcdef" in scratch space. // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`. // into the scratch space. mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672) // Bitmask for detecting `["\"","\\"]`. let e := or(shl(0x22, 1), shl(0x5c, 1)) for {} iszero(eq(s, end)) {} { s := add(s, 1) let c := and(mload(s), 0xff) if iszero(lt(c, 0x20)) { if iszero(and(shl(c, 1), e)) { // Not in `["\"","\\"]`. mstore8(result, c) result := add(result, 1) continue } mstore8(result, 0x5c) // "\\". mstore8(add(result, 1), c) result := add(result, 2) continue } if iszero(and(shl(c, 1), 0x3700)) { // Not in `["\b","\t","\n","\f","\d"]`. mstore8(0x1d, mload(shr(4, c))) // Hex value. mstore8(0x1e, mload(and(c, 15))) // Hex value. mstore(result, mload(0x19)) // "\\u00XX". result := add(result, 6) continue } mstore8(result, 0x5c) // "\\". mstore8(add(result, 1), mload(add(c, 8))) result := add(result, 2) } if addDoubleQuotes { mstore8(result, 34) result := add(1, result) } let last := result mstore(last, 0) // Zeroize the slot after the string. result := mload(0x40) mstore(result, sub(last, add(result, 0x20))) // Store the length. mstore(0x40, add(last, 0x20)) // Allocate the memory. } } /// @dev Escapes the string to be used within double-quotes in a JSON. function escapeJSON(string memory s) internal pure returns (string memory result) { result = escapeJSON(s, false); } /// @dev Returns whether `a` equals `b`. function eq(string memory a, string memory b) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b))) } } /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string. function eqs(string memory a, bytes32 b) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { // These should be evaluated on compile time, as far as possible. let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`. let x := not(or(m, or(b, add(m, and(b, m))))) let r := shl(7, iszero(iszero(shr(128, x)))) r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x)))))) r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffff, shr(r, x)))) r := or(r, shl(3, lt(0xff, shr(r, x)))) // forgefmt: disable-next-item result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))), xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20))))) } } /// @dev Packs a single string with its length into a single word. /// Returns `bytes32(0)` if the length is zero or greater than 31. function packOne(string memory a) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { // We don't need to zero right pad the string, // since this is our own custom non-standard packing scheme. result := mul( // Load the length and the bytes. mload(add(a, 0x1f)), // `length != 0 && length < 32`. Abuses underflow. // Assumes that the length is valid and within the block gas limit. lt(sub(mload(a), 1), 0x1f) ) } } /// @dev Unpacks a string packed using {packOne}. /// Returns the empty string if `packed` is `bytes32(0)`. /// If `packed` is not an output of {packOne}, the output behavior is undefined. function unpackOne(bytes32 packed) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { // Grab the free memory pointer. result := mload(0x40) // Allocate 2 words (1 for the length, 1 for the bytes). mstore(0x40, add(result, 0x40)) // Zeroize the length slot. mstore(result, 0) // Store the length and bytes. mstore(add(result, 0x1f), packed) // Right pad with zeroes. mstore(add(add(result, 0x20), mload(result)), 0) } } /// @dev Packs two strings with their lengths into a single word. /// Returns `bytes32(0)` if combined length is zero or greater than 30. function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { let aLength := mload(a) // We don't need to zero right pad the strings, // since this is our own custom non-standard packing scheme. result := mul( // Load the length and the bytes of `a` and `b`. or( shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))), mload(sub(add(b, 0x1e), aLength)) ), // `totalLength != 0 && totalLength < 31`. Abuses underflow. // Assumes that the lengths are valid and within the block gas limit. lt(sub(add(aLength, mload(b)), 1), 0x1e) ) } } /// @dev Unpacks strings packed using {packTwo}. /// Returns the empty strings if `packed` is `bytes32(0)`. /// If `packed` is not an output of {packTwo}, the output behavior is undefined. function unpackTwo(bytes32 packed) internal pure returns (string memory resultA, string memory resultB) { /// @solidity memory-safe-assembly assembly { // Grab the free memory pointer. resultA := mload(0x40) resultB := add(resultA, 0x40) // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words. mstore(0x40, add(resultB, 0x40)) // Zeroize the length slots. mstore(resultA, 0) mstore(resultB, 0) // Store the lengths and bytes. mstore(add(resultA, 0x1f), packed) mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA)))) // Right pad with zeroes. mstore(add(add(resultA, 0x20), mload(resultA)), 0) mstore(add(add(resultB, 0x20), mload(resultB)), 0) } } /// @dev Directly returns `a` without copying. function directReturn(string memory a) internal pure { assembly { // Assumes that the string does not start from the scratch space. let retStart := sub(a, 0x20) // Right pad with zeroes. Just in case the string is produced // by a method that doesn't zero right pad. mstore(add(retStart, add(mload(a), 0x40)), 0) // Store the return offset. mstore(retStart, 0x20) // End the transaction, returning the string. return(retStart, add(mload(a), 0x60)) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; /// @notice Reentrancy guard mixin. /// @author Soledge (https://github.com/vectorized/soledge/blob/main/src/utils/ReentrancyGuard.sol) /// /// Note: This implementation utilizes the `TSTORE` and `TLOAD` opcodes. /// Please ensure that the chain you are deploying on supports them. abstract contract ReentrancyGuard { /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* CUSTOM ERRORS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Unauthorized reentrant call. error Reentrancy(); /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* STORAGE */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Equivalent to: `uint72(bytes9(keccak256("_REENTRANCY_GUARD_SLOT")))`. /// 9 bytes is large enough to avoid collisions in practice, /// but not too large to result in excessive bytecode bloat. uint256 private constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268; /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* REENTRANCY GUARD */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Guards a function from reentrancy. modifier nonReentrant() virtual { /// @solidity memory-safe-assembly assembly { if tload(_REENTRANCY_GUARD_SLOT) { mstore(0x00, 0xab143c06) // `Reentrancy()`. revert(0x1c, 0x04) } tstore(_REENTRANCY_GUARD_SLOT, address()) } _; /// @solidity memory-safe-assembly assembly { tstore(_REENTRANCY_GUARD_SLOT, 0) } } /// @dev Guards a view function from read-only reentrancy. modifier nonReadReentrant() virtual { /// @solidity memory-safe-assembly assembly { if tload(_REENTRANCY_GUARD_SLOT) { mstore(0x00, 0xab143c06) // `Reentrancy()`. revert(0x1c, 0x04) } } _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Simple single owner authorization mixin. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol) /// /// @dev Note: /// This implementation does NOT auto-initialize the owner to `msg.sender`. /// You MUST call the `_initializeOwner` in the constructor / initializer. /// /// While the ownable portion follows /// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility, /// the nomenclature for the 2-step ownership handover may be unique to this codebase. abstract contract Ownable { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The caller is not authorized to call the function. error Unauthorized(); /// @dev The `newOwner` cannot be the zero address. error NewOwnerIsZeroAddress(); /// @dev The `pendingOwner` does not have a valid handover request. error NoHandoverRequest(); /// @dev Cannot double-initialize. error AlreadyInitialized(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ownership is transferred from `oldOwner` to `newOwner`. /// This event is intentionally kept the same as OpenZeppelin's Ownable to be /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173), /// despite it not being as lightweight as a single argument event. event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); /// @dev An ownership handover to `pendingOwner` has been requested. event OwnershipHandoverRequested(address indexed pendingOwner); /// @dev The ownership handover to `pendingOwner` has been canceled. event OwnershipHandoverCanceled(address indexed pendingOwner); /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`. uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE = 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0; /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE = 0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d; /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE = 0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The owner slot is given by: /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`. /// It is intentionally chosen to be a high value /// to avoid collision with lower slots. /// The choice of manual storage layout is to enable compatibility /// with both regular and upgradeable contracts. bytes32 internal constant _OWNER_SLOT = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927; /// The ownership handover slot of `newOwner` is given by: /// ``` /// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED)) /// let handoverSlot := keccak256(0x00, 0x20) /// ``` /// It stores the expiry timestamp of the two-step ownership handover. uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Override to return true to make `_initializeOwner` prevent double-initialization. function _guardInitializeOwner() internal pure virtual returns (bool guard) {} /// @dev Initializes the owner directly without authorization guard. /// This function must be called upon initialization, /// regardless of whether the contract is upgradeable or not. /// This is to enable generalization to both regular and upgradeable contracts, /// and to save gas in case the initial owner is not the caller. /// For performance reasons, this function will not check if there /// is an existing owner. function _initializeOwner(address newOwner) internal virtual { if (_guardInitializeOwner()) { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT if sload(ownerSlot) { mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`. revert(0x1c, 0x04) } // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Store the new value. sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner)))) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) } } else { /// @solidity memory-safe-assembly assembly { // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Store the new value. sstore(_OWNER_SLOT, newOwner) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) } } } /// @dev Sets the owner directly without authorization guard. function _setOwner(address newOwner) internal virtual { if (_guardInitializeOwner()) { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) // Store the new value. sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner)))) } } else { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) // Store the new value. sstore(ownerSlot, newOwner) } } } /// @dev Throws if the sender is not the owner. function _checkOwner() internal view virtual { /// @solidity memory-safe-assembly assembly { // If the caller is not the stored owner, revert. if iszero(eq(caller(), sload(_OWNER_SLOT))) { mstore(0x00, 0x82b42900) // `Unauthorized()`. revert(0x1c, 0x04) } } } /// @dev Returns how long a two-step ownership handover is valid for in seconds. /// Override to return a different value if needed. /// Made internal to conserve bytecode. Wrap it in a public function if needed. function _ownershipHandoverValidFor() internal view virtual returns (uint64) { return 48 * 3600; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC UPDATE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Allows the owner to transfer the ownership to `newOwner`. function transferOwnership(address newOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { if iszero(shl(96, newOwner)) { mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`. revert(0x1c, 0x04) } } _setOwner(newOwner); } /// @dev Allows the owner to renounce their ownership. function renounceOwnership() public payable virtual onlyOwner { _setOwner(address(0)); } /// @dev Request a two-step ownership handover to the caller. /// The request will automatically expire in 48 hours (172800 seconds) by default. function requestOwnershipHandover() public payable virtual { unchecked { uint256 expires = block.timestamp + _ownershipHandoverValidFor(); /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to `expires`. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), expires) // Emit the {OwnershipHandoverRequested} event. log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller()) } } } /// @dev Cancels the two-step ownership handover to the caller, if any. function cancelOwnershipHandover() public payable virtual { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), 0) // Emit the {OwnershipHandoverCanceled} event. log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller()) } } /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`. /// Reverts if there is no existing ownership handover requested by `pendingOwner`. function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) let handoverSlot := keccak256(0x0c, 0x20) // If the handover does not exist, or has expired. if gt(timestamp(), sload(handoverSlot)) { mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`. revert(0x1c, 0x04) } // Set the handover slot to 0. sstore(handoverSlot, 0) } _setOwner(pendingOwner); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC READ FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the owner of the contract. function owner() public view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { result := sload(_OWNER_SLOT) } } /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`. function ownershipHandoverExpiresAt(address pendingOwner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // Compute the handover slot. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) // Load the handover slot. result := sload(keccak256(0x0c, 0x20)) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MODIFIERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Marks a function as only callable by the owner. modifier onlyOwner() virtual { _checkOwner(); _; } }
{ "remappings": [ "forge-std/=test/utils/forge-std/", "murky/=lib/murky/", "dn404/=lib/dn404/src/", "solady/=lib/solady/src/", "ds-test/=lib/murky/lib/forge-std/lib/ds-test/src/", "forge-std/=lib/murky/lib/forge-std/src/", "murky/=lib/murky/", "openzeppelin-contracts/=lib/murky/lib/openzeppelin-contracts/", "solady/=lib/solady/src/", "soledge/=lib/soledge/src/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "evmVersion": "cancun", "viaIR": false, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountBalanceOverflow","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"InvalidVestingDuration","type":"error"},{"inputs":[],"name":"InvalidVestingMode","type":"error"},{"inputs":[],"name":"Locked","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NotOwnerNorApproved","type":"error"},{"inputs":[],"name":"OnlyOneVaultPerAccount","type":"error"},{"inputs":[],"name":"OverWithdrawn","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[],"name":"SingleTransferNotEnabled","type":"error"},{"inputs":[],"name":"Soulbound","type":"error"},{"inputs":[],"name":"TokenAlreadyExists","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"VaultHasNotBeenCreated","type":"error"},{"inputs":[],"name":"VaultManagerAlreadyInitialized","type":"error"},{"inputs":[],"name":"VaultProxyIsZeroAddress","type":"error"},{"inputs":[],"name":"VestingAlreadyStarted","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"isApproved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"roles","type":"uint256"}],"name":"RolesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"SingleTransferEnabledSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"vault","type":"address"}],"name":"VaultCreated","type":"event"},{"inputs":[],"name":"ADMIN_ROLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ASTERIX","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ASTERIX_MIRROR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"METADATA_SETTER_ROLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"cliffDuration","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cliffDurationLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"createVault","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"createVaultFor","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositAsterixERC20","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositAsterixERC20For","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"depositAsterixMirrorERC721","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"depositAsterixMirrorERC721For","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"directResetVestingState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"mode","type":"uint8"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"}],"name":"directResetVestingState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"directSetInitialLockedAsterixERC20Amount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"earlyVestingDuration","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"earlyVestingDurationLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"grantRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAllRoles","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAnyRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"initialLockedAsterixERC20Amount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockCliffDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lockEarlyVestingDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lockVaultProxy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lockVestingDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"maxAsterixERC20WithdrawableWithoutBurningERC721s","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"renounceRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"requiredAsterixERC20Balance","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"revokeRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"rolesOf","outputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"uri","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"value","type":"uint32"}],"name":"setCliffDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"value","type":"uint32"}],"name":"setEarlyVestingDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"}],"name":"setNameAndSymbol","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"}],"name":"setSingleTransferEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"renderer","type":"address"}],"name":"setTokenURIRenderer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"proxy","type":"address"}],"name":"setVaultProxy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"value","type":"uint32"}],"name":"setVestingDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"singleTransferEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startCliff","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startEarlyVesting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"result","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalVaults","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"vaultAt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"vaultOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"vaultOwner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultProxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultProxyLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"vaultTokenId","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingDuration","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingDurationLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingEnd","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingMode","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingStart","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawAsterixERC20","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawAsterixERC20To","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"withdrawAsterixMirrorERC721","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"withdrawAsterixMirrorERC721To","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"withdrawableAsterixERC20","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
608060405234801561000f575f80fd5b5060058054600160481b600160a81b03191674013c6800018b820000ed4e0000000000000000000017905561005f336effe8b47b3e2130213b802212439497146100595733610064565b32610064565b61013d565b61006d8161007e565b61007b8163800000006100d6565b50565b638b78c6d81980541561009857630dc149f05f526004601cfd5b6001600160a01b03909116801560ff1b8117909155805f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b6100e2828260016100e6565b5050565b638b78c6d8600c52825f526020600c20805483811783610107575080841681185b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f80a3505050505050565b612b348061014a5f395ff3fe608060405260043610610458575f3560e01c806370a0823111610241578063aeb84f2411610134578063d85349f7116100b3578063f8e2567911610078578063f8e2567914610d3c578063fbcd215914610d5b578063fee81cf414610d7a578063ff28736114610dab578063ff87a5f514610dbf575f80fd5b8063d85349f714610cb3578063dc91c88f14610cd6578063e985e9c514610cf5578063f04e283e14610d16578063f2fde38b14610d29575f80fd5b8063b88d4fde116100f9578063b88d4fde14610c24578063c597578814610c37578063c87b56dd14610c56578063ccc7397414610c75578063cf70698a14610c94575f80fd5b8063aeb84f2414610b8d578063b19c3ca314610bac578063b1f047dd14610bc6578063b50fd68114610bf1578063b69e064314610c10575f80fd5b80638da5cb5b116101c0578063983525bf11610185578063983525bf14610acc578063a22cb46514610aeb578063a6caeb4b14610b05578063a7e76d4814610b42578063ae9e38ae14610b62575f80fd5b80638da5cb5b14610a3d578063905e344114610a5557806394a7c3ef14610a7757806395d89b4114610a995780639636d88114610aad575f80fd5b806384a1931f1161020657806384a1931f1461097f578063861145cd146109a2578063893a5372146109c15780638c13a453146109e75780638d65402314610a06575f80fd5b806370a0823114610902578063711039a614610921578063715018a61461094057806375b238fc1461094857806377b04c8f1461095c575f80fd5b806329a4927a116103595780634c5cd306116102d857806355f804b31161029d57806355f804b3146108715780635a446215146108905780635d12928b146108af5780635e3318f8146108c35780636352211e146108e3575f80fd5b80634c5cd306146107ed57806350a534c714610801578063514e62fc1461082057806353ff9fcb1461085557806354d1f13d14610869575f80fd5b8063397bfe551161031e578063397bfe5514610769578063398e6cb71461078857806342842e0e146107a757806347f7d14a146107ba5780634a4ee7b1146107da575f80fd5b806329a4927a146106c35780632de94807146106e2578063302ad53a14610713578063354b7f941461072a57806336fe3cb81461074a575f80fd5b806318160ddd116103e55780631cd64df4116103aa5780631cd64df41461064357806320cae9081461067857806323b872dd1461068c578063254800d41461069a57806325692962146106bb575f80fd5b806318160ddd146105c3578063183a4f6e146105ea57806318da2d98146105fd5780631a966d70146106115780631c10893f14610630575f80fd5b806308c0c05f1161042b57806308c0c05f14610524578063095ea7b31461053a57806309d2851a1461054d57806310a96a011461056c5780631514617e1461058b575f80fd5b806301ffc9a71461045c57806306fdde03146104ad5780630709df45146104ce578063081812fc14610505575b5f80fd5b348015610467575f80fd5b50610498610476366004612583565b6301ffc9a760e09190911c9081146380ac58cd821417635b5e139f9091141790565b60405190151581526020015b60405180910390f35b3480156104b8575f80fd5b506104c1610dde565b6040516104a491906125aa565b3480156104d9575f80fd5b506104ed6104e83660046125f5565b610e6e565b6040516001600160a01b0390911681526020016104a4565b348015610510575f80fd5b506104ed61051f36600461260e565b505f90565b34801561052f575f80fd5b50610538610e7e565b005b610538610548366004612625565b610eb5565b348015610558575f80fd5b50610538610567366004612660565b610ed2565b348015610577575f80fd5b506104ed6105863660046126c0565b610f6f565b348015610596575f80fd5b506005546105ae90600160681b900463ffffffff1681565b60405163ffffffff90911681526020016104a4565b3480156105ce575f80fd5b506004546001600160401b03165b6040519081526020016104a4565b6105386105f836600461260e565b61101b565b348015610608575f80fd5b50610538611028565b34801561061c575f80fd5b506104ed61062b3660046125f5565b611049565b61053861063e366004612625565b61108f565b34801561064e575f80fd5b5061049861065d366004612625565b638b78c6d8600c9081525f9290925260209091205481161490565b348015610683575f80fd5b506105386110a1565b61053861054836600461270e565b3480156106a5575f80fd5b506005546105ae90610100900463ffffffff1681565b6105386110c7565b3480156106ce575f80fd5b506104ed6106dd3660046126c0565b611113565b3480156106ed575f80fd5b506105dc6106fc3660046125f5565b638b78c6d8600c9081525f91909152602090205490565b34801561071e575f80fd5b506105dc638000000081565b348015610735575f80fd5b5060045461049890600160e81b900460ff1681565b348015610755575f80fd5b506105dc6107643660046125f5565b611186565b348015610774575f80fd5b506105386107833660046125f5565b6111cb565b348015610793575f80fd5b506104ed6107a236600461260e565b611233565b6105386107b536600461270e565b61123e565b3480156107c5575f80fd5b5060045461049890600160e01b900460ff1681565b6105386107e8366004612625565b61126a565b3480156107f8575f80fd5b5061053861127c565b34801561080c575f80fd5b506105dc61081b3660046125f5565b61129d565b34801561082b575f80fd5b5061049861083a366004612625565b638b78c6d8600c9081525f9290925260209091205416151590565b348015610860575f80fd5b50610538611304565b6105386113cb565b34801561087c575f80fd5b5061053861088b366004612784565b611404565b34801561089b575f80fd5b506105386108aa3660046127c2565b611425565b3480156108ba575f80fd5b506104ed611456565b3480156108ce575f80fd5b5060045461049890600160f01b900460ff1681565b3480156108ee575f80fd5b506104ed6108fd36600461260e565b611465565b34801561090d575f80fd5b506105dc61091c3660046125f5565b611488565b34801561092c575f80fd5b5061053861093b366004612660565b6114c0565b610538611557565b348015610953575f80fd5b506105dc600181565b348015610967575f80fd5b506005546105ae90600160881b900463ffffffff1681565b34801561098a575f80fd5b506005546105ae90600160281b900463ffffffff1681565b3480156109ad575f80fd5b506105dc6109bc3660046125f5565b61156a565b3480156109cc575f80fd5b506004546104ed90600160401b90046001600160a01b031681565b3480156109f2575f80fd5b506104ed610a0136600461260e565b6115c2565b348015610a11575f80fd5b50600454610a25906001600160401b031681565b6040516001600160401b0390911681526020016104a4565b348015610a48575f80fd5b50638b78c6d819546104ed565b348015610a60575f80fd5b506104ed6eca73a6df4c58b84c5b4b847fe8ff3981565b348015610a82575f80fd5b506104ed6ec26fabfe894d13233d5ec73f61cc7281565b348015610aa4575f80fd5b506104c16115d4565b348015610ab8575f80fd5b50610538610ac7366004612660565b6115e3565b348015610ad7575f80fd5b506104ed610ae6366004612625565b61167a565b348015610af6575f80fd5b50610538610548366004612837565b348015610b10575f80fd5b50610498610b1f36600461260e565b5f818152673ec412a9852d173d60c11b601c52602090208101015460a01c151590565b348015610b4d575f80fd5b5060045461049890600160f81b900460ff1681565b348015610b6d575f80fd5b50600554610b7b9060ff1681565b60405160ff90911681526020016104a4565b348015610b98575f80fd5b506104ed610ba7366004612868565b61170b565b348015610bb7575f80fd5b5061053861054836600461289a565b348015610bd1575f80fd5b506105dc610be03660046125f5565b60066020525f908152604090205481565b348015610bfc575f80fd5b506104ed610c0b36600461260e565b611717565b348015610c1b575f80fd5b50610538611722565b610538610c323660046128b3565b611745565b348015610c42575f80fd5b506104ed610c513660046125f5565b61179f565b348015610c61575f80fd5b506104c1610c7036600461260e565b6117e0565b348015610c80575f80fd5b50610538610c8f366004612625565b61195b565b348015610c9f575f80fd5b506104ed610cae366004612868565b6119d6565b348015610cbe575f80fd5b506005546105ae90600160481b900463ffffffff1681565b348015610ce1575f80fd5b50610538610cf036600461291c565b6119e2565b348015610d00575f80fd5b50610498610d0f366004612963565b5f92915050565b610538610d243660046125f5565b611aab565b610538610d373660046125f5565b611ae5565b348015610d47575f80fd5b506105dc610d563660046125f5565b611b0b565b348015610d66575f80fd5b506104ed610d75366004612625565b611b66565b348015610d85575f80fd5b506105dc610d943660046125f5565b63389a75e1600c9081525f91909152602090205490565b348015610db6575f80fd5b50610538611bbf565b348015610dca575f80fd5b50610538610dd93660046125f5565b611c92565b606060018054610ded9061298b565b80601f0160208091040260200160405190810160405280929190818152602001828054610e199061298b565b8015610e645780601f10610e3b57610100808354040283529160200191610e64565b820191905f5260205f20905b815481529060010190602001808311610e4757829003601f168201915b5050505050905090565b5f610e7882611cc2565b92915050565b6001610e8981611ce2565b610e91611d13565b506005805468ffffffffffffffffff191690556004805462ffffff60e01b19169055565b60405163a4420a9560e01b815260040160405180910390fd5b5050565b6001610edd81611ce2565b600454600160e01b900460ff1615610f08576040516303cb96db60e21b815260040160405180910390fd5b63ffffffff82161580610f2457506301e1338063ffffffff8316115b15610f4257604051632b39f6cb60e01b815260040160405180910390fd5b506005805463ffffffff909216600160481b026cffffffff00000000000000000019909216919091179055565b5f68929eee149b4bd212685c15610f8d5763ab143c065f526004601cfd5b3068929eee149b4bd212685d610fa233610e6e565b90506001600160a01b038116610fcb5760405163425ef92760e01b815260040160405180910390fd5b5f5b82811015610fff57610ff7826ec26fabfe894d13233d5ec73f61cc7287600585901b880135611d7c565b600101610fcd565b50611008611da0565b5f68929eee149b4bd212685d9392505050565b6110253382611e27565b50565b600161103381611ce2565b506004805460ff60f01b1916600160f01b179055565b5f68929eee149b4bd212685c156110675763ab143c065f526004601cfd5b3068929eee149b4bd212685d61107c82611e32565b90505f68929eee149b4bd212685d919050565b611097611f16565b610ece8282611f30565b60016110ac81611ce2565b506004805460ff60e81b1916600160e81b179055565b505050565b5f6202a3006001600160401b03164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a250565b5f68929eee149b4bd212685c156111315763ab143c065f526004601cfd5b3068929eee149b4bd212685d61114684611e32565b90505f5b8281101561117c57611174826ec26fabfe894d13233d5ec73f61cc7233600585901b880135611f3c565b60010161114a565b5061100884611f7d565b5f8061119183612024565b90505f61119d82612033565b9050836001600160a01b03166111b282610e6e565b6001600160a01b0316036111c4578192505b5050919050565b60016111d681611ce2565b600454600160f81b900460ff1615611201576040516303cb96db60e21b815260040160405180910390fd5b50600480546001600160a01b03909216600160401b0268010000000000000000600160e01b0319909216919091179055565b5f610e78338361167a565b611249838383610eb5565b813b156110c2576110c283838360405180602001604052805f81525061205a565b611272611f16565b610ece8282611e27565b600161128781611ce2565b506004805460ff60e01b1916600160e01b179055565b5f806112a883610e6e565b90506001600160a01b0381166112c057505f92915050565b670de0b6b3a76400006112e26ec26fabfe894d13233d5ec73f61cc72836120db565b026112fc6eca73a6df4c58b84c5b4b847fe8ff39836120db565b039392505050565b600161130f81611ce2565b60055460ff161561133357604051636752fae160e11b815260040160405180910390fd5b6005545f9061134f90600160481b900463ffffffff16426129bd565b6005805463ffffffff8084166101000264ffffffffff19909216919091176001179182905591925061138a91600160681b90910416826129bd565b6005805468ffffffff00000000001916600160281b63ffffffff939093169290920291909117905550506004805461ffff60e01b191661010160e01b179055565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2565b638000000061141281611ce2565b600361141f838583612a45565b50505050565b638000000061143381611ce2565b6001611440858783612a45565b50600261144e838583612a45565b505050505050565b5f61146033611049565b905090565b5f61146f82612033565b9050806114835763ceea21b65f526004601cfd5b919050565b5f8161149b57638f4eb6045f526004601cfd5b673ec412a9852d173d60c11b601c52815f5263ffffffff601c600c2054169050919050565b60016114cb81611ce2565b600454600160f01b900460ff16156114f6576040516303cb96db60e21b815260040160405180910390fd5b63ffffffff8216158061151257506301e1338063ffffffff8316115b1561153057604051632b39f6cb60e01b815260040160405180910390fd5b506005805463ffffffff909216600160881b0263ffffffff60881b19909216919091179055565b61155f611f16565b6115685f612105565b565b5f8061157583610e6e565b90506001600160a01b03811661158d57505f92915050565b6115bb6115a96eca73a6df4c58b84c5b4b847fe8ff39836120db565b6115b285611b0b565b80821191030290565b9392505050565b5f610e786115cf83612033565b611cc2565b606060028054610ded9061298b565b60016115ee81611ce2565b600454600160e81b900460ff1615611619576040516303cb96db60e21b815260040160405180910390fd5b63ffffffff8216158061163557506301e1338063ffffffff8316115b1561165357604051632b39f6cb60e01b815260040160405180910390fd5b506005805463ffffffff909216600160681b0263ffffffff60681b19909216919091179055565b5f68929eee149b4bd212685c156116985763ab143c065f526004601cfd5b3068929eee149b4bd212685d6116ad33610e6e565b90506001600160a01b0381166116d65760405163425ef92760e01b815260040160405180910390fd5b6116f1816eca73a6df4c58b84c5b4b847fe8ff39858561214b565b6116f9611da0565b5f68929eee149b4bd212685d92915050565b5f6115bb338484610f6f565b5f610e783383611b66565b600161172d81611ce2565b50600480546001600160f81b0316600160f81b179055565b611750858585610eb5565b833b156117985761179885858585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061205a92505050565b5050505050565b5f806117aa83612024565b90505f6117b682612033565b9050836001600160a01b03166117cb82610e6e565b6001600160a01b0316036111c4579392505050565b6060611809825f818152673ec412a9852d173d60c11b601c52602090208101015460601b151590565b6118265760405163677510db60e11b815260040160405180910390fd5b5f546001600160a01b0316801561188a57604051915063c87b56dd5f52826020525f806024601c845afa61185c573d5f833e3d82fd5b60205f803e60205f51833e815160205f5101602084013e815160208301015f81526020810160405250611955565b600380546118979061298b565b159050611955576115bb600380546118ae9061298b565b80601f01602080910402602001604051908101604052809291908181526020018280546118da9061298b565b80156119255780601f106118fc57610100808354040283529160200191611925565b820191905f5260205f20905b81548152906001019060200180831161190857829003601f168201915b5050505050604051806040016040528060048152602001637b69647d60e01b8152506119508661216f565b6121b1565b50919050565b600161196681611ce2565b61196e611d13565b600160601b82106119ba5760405162461bcd60e51b81526020600482015260116024820152703030b6b7bab73a30103a37b7903134b39760791b60448201526064015b60405180910390fd5b506001600160a01b039091165f90815260066020526040902055565b5f6115bb338484611113565b60016119ed81611ce2565b6119f5611d13565b611a008360016129bd565b63ffffffff168263ffffffff161015611a5b5760405162461bcd60e51b815260206004820152601d60248201527f60656e6460206d757374206265203e3d20607374617274202b2031602e00000060448201526064016119b1565b506005805460ff9490941664ffffffffff199094169390931761010063ffffffff938416021768ffffffff00000000001916600160281b91909216021790556004805462ffffff60e01b19169055565b611ab3611f16565b63389a75e1600c52805f526020600c208054421115611ad957636f5e88185f526004601cfd5b5f905561102581612105565b611aed611f16565b8060601b611b0257637448fbae5f526004601cfd5b61102581612105565b6005545f9060ff161561148357506001600160a01b0381165f9081526006602052604090205460055463ffffffff600160281b82048116916101009004164281116111c4578082034280841190840302840204949350505050565b5f68929eee149b4bd212685c15611b845763ab143c065f526004601cfd5b3068929eee149b4bd212685d611b9983611e32565b9050611bb6816eca73a6df4c58b84c5b4b847fe8ff393385611f3c565b6116f983611f7d565b6001611bca81611ce2565b60055460ff16600114611bf057604051636752fae160e11b815260040160405180910390fd5b600554610100900463ffffffff164210611c1d576040516372de7acd60e01b815260040160405180910390fd5b6005805463ffffffff428181166101000264ffffffffff199093169290921760021792839055611c5592600160881b900416906129bd565b6005805463ffffffff92909216600160281b0268ffffffff000000000019909216919091179055506004805460ff60f01b1916600160f01b179055565b6380000000611ca081611ce2565b505f80546001600160a01b0319166001600160a01b0392909216919091179055565b673ec412a9852d173d60c11b601c9081525f91909152600c205460201c90565b638b78c6d81954331461102557638b78c6d8600c52335f52806020600c205416611025576382b429005f526004601cfd5b466142681480611d2557504662aa36a7145b6115685760405162461bcd60e51b815260206004820152602260248201527f4f6e6c79206f6e205365706f6c6961202f20486f6c65736b7920746573746e656044820152613a1760f11b60648201526084016119b1565b61141f84846323b872dd876001600160a01b0316866001600160a01b0316866122d1565b5f611daa33610e6e565b60055490915060ff16611de257611dd06eca73a6df4c58b84c5b4b847fe8ff39826120db565b335f9081526006602052604090205550565b5f611dec33611b0b565b905080611e086eca73a6df4c58b84c5b4b847fe8ff39846120db565b1015610ece57604051631909a90560e01b815260040160405180910390fd5b610ece82825f612338565b5f611e3c82611cc2565b90506001600160a01b03811615611e5257919050565b600454600160401b90046001600160a01b031680611e8357604051630873153160e11b815260040160405180910390fd5b600480546001600160401b038082166001011667ffffffffffffffff199091168117909155611eb384825f61238f565b611ec0825f463085612421565b9250826001600160a01b031681856001600160a01b03167f019b61d9140f8784f4eed1e66569335630452af2d974ceb13d3e706c0786729660405160405180910390a46111c484846001600160a01b0316612493565b638b78c6d819543314611568576382b429005f526004601cfd5b610ece82826001612338565b60405181606052846040528260601b602c526323b872dd60601b600c5260205f6064601c5f885af1611f70573d5f823e3d81fd5b5f60605260405250505050565b5f611f9f6eca73a6df4c58b84c5b4b847fe8ff39611f9a84610e6e565b6120db565b60055490915060ff161580611fc05750600554610100900463ffffffff1642105b8061200357506001600160a01b0382165f90815260066020526040902054811180156120035750638b78c6d819546001600160a01b0316336001600160a01b0316145b15610ece576001600160a01b03919091165f90815260066020526040902055565b5f6020608d5f843c50505f5190565b5f818152673ec412a9852d173d60c11b601c5260209020810101546001600160a01b031690565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a084015280156120a1578060c08401826020870160045afa505b60208360a48301601c86015f8a5af16120c2573d156120c2573d5f843e3d83fd5b508060e01b82511461144e5763d1a57ed65f526004601cfd5b5f816014526370a0823160601b5f5260208060246010865afa601f3d111660205102905092915050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3811560ff1b8217905550565b61141f848463a9059cbb856001600160a01b0316856001600160a01b03165f6122d1565b60606080604051019050602081016040525f8152805f19835b928101926030600a8206018453600a900480612188575050819003601f19909101908152919050565b60608351835183516020870196506020860195506020850194506020604051019350828701838311612281576001838203015f602085106121f157508388205b601f851660200360031b89515b8b51818118831c6122685783156122385783888e201461223857808a5260019c8d019c90990198848d10612232575061227c565b506121fe565b5f5b8b8101518b82015260200187811061223a57509b87019b98860198871561226857848d10612232575061227c565b895260019b8c019b90980197838c106121fe575b505050505b84935060206040510194508781038585030192505b808810156122b1578751845260209788019790930192612296565b50505f818401908152602001604052601f19909201918252509392505050565b604051856014820152635194544760601b81525f6034820152608060548201525f60748201528460988201528360b88201528260d88201528160f8820152606460948201525f38610124601084015f8b5af161232f573d5f823e3d81fd5b50505050505050565b638b78c6d8600c52825f526020600c20805483811783612359575080841681185b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f80a3505050505050565b61239a5f84846124bf565b8260601b60601c9250815f52673ec412a9852d173d60c11b601c52828160a01b1760205f208301830155825f52601c600c20600181540163ffffffff811685026123f35767ea553b3401336cea851560021b526004601cfd5b905581835f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8138a4505050565b5f6040518660148201528560348201528460548201528360601b60601c6074820152826094820152638a54c52f60601b815260205f60a4601084015f6f6551c19487814612e58fe068137757585af1601f3d1116612486576320188a595f526004601cfd5b50505f5195945050505050565b673ec412a9852d173d60c11b601c52815f52601c600c2080548060201c831860201b8118825550505050565b6124c882611cc2565b6001600160e01b0316156124ef5760405163759be05b60e11b815260040160405180910390fd5b6001600160a01b038316156110c2575f818152673ec412a9852d173d60c11b601c5260208120820182015460a01c900361253c5760405163768a0e2b60e01b815260040160405180910390fd5b5f818152673ec412a9852d173d60c11b601c52602090208101810180546001600160a01b031981161890556125798261257485611cc2565b612493565b6110c2835f612493565b5f60208284031215612593575f80fd5b81356001600160e01b0319811681146115bb575f80fd5b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b80356001600160a01b0381168114611483575f80fd5b5f60208284031215612605575f80fd5b6115bb826125df565b5f6020828403121561261e575f80fd5b5035919050565b5f8060408385031215612636575f80fd5b61263f836125df565b946020939093013593505050565b803563ffffffff81168114611483575f80fd5b5f60208284031215612670575f80fd5b6115bb8261264d565b5f8083601f840112612689575f80fd5b5081356001600160401b0381111561269f575f80fd5b6020830191508360208260051b85010111156126b9575f80fd5b9250929050565b5f805f604084860312156126d2575f80fd5b6126db846125df565b925060208401356001600160401b038111156126f5575f80fd5b61270186828701612679565b9497909650939450505050565b5f805f60608486031215612720575f80fd5b612729846125df565b9250612737602085016125df565b9150604084013590509250925092565b5f8083601f840112612757575f80fd5b5081356001600160401b0381111561276d575f80fd5b6020830191508360208285010111156126b9575f80fd5b5f8060208385031215612795575f80fd5b82356001600160401b038111156127aa575f80fd5b6127b685828601612747565b90969095509350505050565b5f805f80604085870312156127d5575f80fd5b84356001600160401b03808211156127eb575f80fd5b6127f788838901612747565b9096509450602087013591508082111561280f575f80fd5b5061281c87828801612747565b95989497509550505050565b80358015158114611483575f80fd5b5f8060408385031215612848575f80fd5b612851836125df565b915061285f60208401612828565b90509250929050565b5f8060208385031215612879575f80fd5b82356001600160401b0381111561288e575f80fd5b6127b685828601612679565b5f602082840312156128aa575f80fd5b6115bb82612828565b5f805f805f608086880312156128c7575f80fd5b6128d0866125df565b94506128de602087016125df565b93506040860135925060608601356001600160401b038111156128ff575f80fd5b61290b88828901612747565b969995985093965092949392505050565b5f805f6060848603121561292e575f80fd5b833560ff8116811461293e575f80fd5b925061294c6020850161264d565b915061295a6040850161264d565b90509250925092565b5f8060408385031215612974575f80fd5b61297d836125df565b915061285f602084016125df565b600181811c9082168061299f57607f821691505b60208210810361195557634e487b7160e01b5f52602260045260245ffd5b63ffffffff8181168382160190808211156129e657634e487b7160e01b5f52601160045260245ffd5b5092915050565b634e487b7160e01b5f52604160045260245ffd5b601f8211156110c257805f5260205f20601f840160051c81016020851015612a265750805b601f840160051c820191505b81811015611798575f8155600101612a32565b6001600160401b03831115612a5c57612a5c6129ed565b612a7083612a6a835461298b565b83612a01565b5f601f841160018114612aa1575f8515612a8a5750838201355b5f19600387901b1c1916600186901b178355611798565b5f83815260208120601f198716915b82811015612ad05786850135825560209485019460019092019101612ab0565b5086821015612aec575f1960f88860031b161c19848701351681555b505060018560011b018355505050505056fea264697066735822122077798a98c56e954f07284ba2e0b0f9e9ae12504a117f14e1589d59f78e07566464736f6c63430008190033
Deployed Bytecode
0x608060405260043610610458575f3560e01c806370a0823111610241578063aeb84f2411610134578063d85349f7116100b3578063f8e2567911610078578063f8e2567914610d3c578063fbcd215914610d5b578063fee81cf414610d7a578063ff28736114610dab578063ff87a5f514610dbf575f80fd5b8063d85349f714610cb3578063dc91c88f14610cd6578063e985e9c514610cf5578063f04e283e14610d16578063f2fde38b14610d29575f80fd5b8063b88d4fde116100f9578063b88d4fde14610c24578063c597578814610c37578063c87b56dd14610c56578063ccc7397414610c75578063cf70698a14610c94575f80fd5b8063aeb84f2414610b8d578063b19c3ca314610bac578063b1f047dd14610bc6578063b50fd68114610bf1578063b69e064314610c10575f80fd5b80638da5cb5b116101c0578063983525bf11610185578063983525bf14610acc578063a22cb46514610aeb578063a6caeb4b14610b05578063a7e76d4814610b42578063ae9e38ae14610b62575f80fd5b80638da5cb5b14610a3d578063905e344114610a5557806394a7c3ef14610a7757806395d89b4114610a995780639636d88114610aad575f80fd5b806384a1931f1161020657806384a1931f1461097f578063861145cd146109a2578063893a5372146109c15780638c13a453146109e75780638d65402314610a06575f80fd5b806370a0823114610902578063711039a614610921578063715018a61461094057806375b238fc1461094857806377b04c8f1461095c575f80fd5b806329a4927a116103595780634c5cd306116102d857806355f804b31161029d57806355f804b3146108715780635a446215146108905780635d12928b146108af5780635e3318f8146108c35780636352211e146108e3575f80fd5b80634c5cd306146107ed57806350a534c714610801578063514e62fc1461082057806353ff9fcb1461085557806354d1f13d14610869575f80fd5b8063397bfe551161031e578063397bfe5514610769578063398e6cb71461078857806342842e0e146107a757806347f7d14a146107ba5780634a4ee7b1146107da575f80fd5b806329a4927a146106c35780632de94807146106e2578063302ad53a14610713578063354b7f941461072a57806336fe3cb81461074a575f80fd5b806318160ddd116103e55780631cd64df4116103aa5780631cd64df41461064357806320cae9081461067857806323b872dd1461068c578063254800d41461069a57806325692962146106bb575f80fd5b806318160ddd146105c3578063183a4f6e146105ea57806318da2d98146105fd5780631a966d70146106115780631c10893f14610630575f80fd5b806308c0c05f1161042b57806308c0c05f14610524578063095ea7b31461053a57806309d2851a1461054d57806310a96a011461056c5780631514617e1461058b575f80fd5b806301ffc9a71461045c57806306fdde03146104ad5780630709df45146104ce578063081812fc14610505575b5f80fd5b348015610467575f80fd5b50610498610476366004612583565b6301ffc9a760e09190911c9081146380ac58cd821417635b5e139f9091141790565b60405190151581526020015b60405180910390f35b3480156104b8575f80fd5b506104c1610dde565b6040516104a491906125aa565b3480156104d9575f80fd5b506104ed6104e83660046125f5565b610e6e565b6040516001600160a01b0390911681526020016104a4565b348015610510575f80fd5b506104ed61051f36600461260e565b505f90565b34801561052f575f80fd5b50610538610e7e565b005b610538610548366004612625565b610eb5565b348015610558575f80fd5b50610538610567366004612660565b610ed2565b348015610577575f80fd5b506104ed6105863660046126c0565b610f6f565b348015610596575f80fd5b506005546105ae90600160681b900463ffffffff1681565b60405163ffffffff90911681526020016104a4565b3480156105ce575f80fd5b506004546001600160401b03165b6040519081526020016104a4565b6105386105f836600461260e565b61101b565b348015610608575f80fd5b50610538611028565b34801561061c575f80fd5b506104ed61062b3660046125f5565b611049565b61053861063e366004612625565b61108f565b34801561064e575f80fd5b5061049861065d366004612625565b638b78c6d8600c9081525f9290925260209091205481161490565b348015610683575f80fd5b506105386110a1565b61053861054836600461270e565b3480156106a5575f80fd5b506005546105ae90610100900463ffffffff1681565b6105386110c7565b3480156106ce575f80fd5b506104ed6106dd3660046126c0565b611113565b3480156106ed575f80fd5b506105dc6106fc3660046125f5565b638b78c6d8600c9081525f91909152602090205490565b34801561071e575f80fd5b506105dc638000000081565b348015610735575f80fd5b5060045461049890600160e81b900460ff1681565b348015610755575f80fd5b506105dc6107643660046125f5565b611186565b348015610774575f80fd5b506105386107833660046125f5565b6111cb565b348015610793575f80fd5b506104ed6107a236600461260e565b611233565b6105386107b536600461270e565b61123e565b3480156107c5575f80fd5b5060045461049890600160e01b900460ff1681565b6105386107e8366004612625565b61126a565b3480156107f8575f80fd5b5061053861127c565b34801561080c575f80fd5b506105dc61081b3660046125f5565b61129d565b34801561082b575f80fd5b5061049861083a366004612625565b638b78c6d8600c9081525f9290925260209091205416151590565b348015610860575f80fd5b50610538611304565b6105386113cb565b34801561087c575f80fd5b5061053861088b366004612784565b611404565b34801561089b575f80fd5b506105386108aa3660046127c2565b611425565b3480156108ba575f80fd5b506104ed611456565b3480156108ce575f80fd5b5060045461049890600160f01b900460ff1681565b3480156108ee575f80fd5b506104ed6108fd36600461260e565b611465565b34801561090d575f80fd5b506105dc61091c3660046125f5565b611488565b34801561092c575f80fd5b5061053861093b366004612660565b6114c0565b610538611557565b348015610953575f80fd5b506105dc600181565b348015610967575f80fd5b506005546105ae90600160881b900463ffffffff1681565b34801561098a575f80fd5b506005546105ae90600160281b900463ffffffff1681565b3480156109ad575f80fd5b506105dc6109bc3660046125f5565b61156a565b3480156109cc575f80fd5b506004546104ed90600160401b90046001600160a01b031681565b3480156109f2575f80fd5b506104ed610a0136600461260e565b6115c2565b348015610a11575f80fd5b50600454610a25906001600160401b031681565b6040516001600160401b0390911681526020016104a4565b348015610a48575f80fd5b50638b78c6d819546104ed565b348015610a60575f80fd5b506104ed6eca73a6df4c58b84c5b4b847fe8ff3981565b348015610a82575f80fd5b506104ed6ec26fabfe894d13233d5ec73f61cc7281565b348015610aa4575f80fd5b506104c16115d4565b348015610ab8575f80fd5b50610538610ac7366004612660565b6115e3565b348015610ad7575f80fd5b506104ed610ae6366004612625565b61167a565b348015610af6575f80fd5b50610538610548366004612837565b348015610b10575f80fd5b50610498610b1f36600461260e565b5f818152673ec412a9852d173d60c11b601c52602090208101015460a01c151590565b348015610b4d575f80fd5b5060045461049890600160f81b900460ff1681565b348015610b6d575f80fd5b50600554610b7b9060ff1681565b60405160ff90911681526020016104a4565b348015610b98575f80fd5b506104ed610ba7366004612868565b61170b565b348015610bb7575f80fd5b5061053861054836600461289a565b348015610bd1575f80fd5b506105dc610be03660046125f5565b60066020525f908152604090205481565b348015610bfc575f80fd5b506104ed610c0b36600461260e565b611717565b348015610c1b575f80fd5b50610538611722565b610538610c323660046128b3565b611745565b348015610c42575f80fd5b506104ed610c513660046125f5565b61179f565b348015610c61575f80fd5b506104c1610c7036600461260e565b6117e0565b348015610c80575f80fd5b50610538610c8f366004612625565b61195b565b348015610c9f575f80fd5b506104ed610cae366004612868565b6119d6565b348015610cbe575f80fd5b506005546105ae90600160481b900463ffffffff1681565b348015610ce1575f80fd5b50610538610cf036600461291c565b6119e2565b348015610d00575f80fd5b50610498610d0f366004612963565b5f92915050565b610538610d243660046125f5565b611aab565b610538610d373660046125f5565b611ae5565b348015610d47575f80fd5b506105dc610d563660046125f5565b611b0b565b348015610d66575f80fd5b506104ed610d75366004612625565b611b66565b348015610d85575f80fd5b506105dc610d943660046125f5565b63389a75e1600c9081525f91909152602090205490565b348015610db6575f80fd5b50610538611bbf565b348015610dca575f80fd5b50610538610dd93660046125f5565b611c92565b606060018054610ded9061298b565b80601f0160208091040260200160405190810160405280929190818152602001828054610e199061298b565b8015610e645780601f10610e3b57610100808354040283529160200191610e64565b820191905f5260205f20905b815481529060010190602001808311610e4757829003601f168201915b5050505050905090565b5f610e7882611cc2565b92915050565b6001610e8981611ce2565b610e91611d13565b506005805468ffffffffffffffffff191690556004805462ffffff60e01b19169055565b60405163a4420a9560e01b815260040160405180910390fd5b5050565b6001610edd81611ce2565b600454600160e01b900460ff1615610f08576040516303cb96db60e21b815260040160405180910390fd5b63ffffffff82161580610f2457506301e1338063ffffffff8316115b15610f4257604051632b39f6cb60e01b815260040160405180910390fd5b506005805463ffffffff909216600160481b026cffffffff00000000000000000019909216919091179055565b5f68929eee149b4bd212685c15610f8d5763ab143c065f526004601cfd5b3068929eee149b4bd212685d610fa233610e6e565b90506001600160a01b038116610fcb5760405163425ef92760e01b815260040160405180910390fd5b5f5b82811015610fff57610ff7826ec26fabfe894d13233d5ec73f61cc7287600585901b880135611d7c565b600101610fcd565b50611008611da0565b5f68929eee149b4bd212685d9392505050565b6110253382611e27565b50565b600161103381611ce2565b506004805460ff60f01b1916600160f01b179055565b5f68929eee149b4bd212685c156110675763ab143c065f526004601cfd5b3068929eee149b4bd212685d61107c82611e32565b90505f68929eee149b4bd212685d919050565b611097611f16565b610ece8282611f30565b60016110ac81611ce2565b506004805460ff60e81b1916600160e81b179055565b505050565b5f6202a3006001600160401b03164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a250565b5f68929eee149b4bd212685c156111315763ab143c065f526004601cfd5b3068929eee149b4bd212685d61114684611e32565b90505f5b8281101561117c57611174826ec26fabfe894d13233d5ec73f61cc7233600585901b880135611f3c565b60010161114a565b5061100884611f7d565b5f8061119183612024565b90505f61119d82612033565b9050836001600160a01b03166111b282610e6e565b6001600160a01b0316036111c4578192505b5050919050565b60016111d681611ce2565b600454600160f81b900460ff1615611201576040516303cb96db60e21b815260040160405180910390fd5b50600480546001600160a01b03909216600160401b0268010000000000000000600160e01b0319909216919091179055565b5f610e78338361167a565b611249838383610eb5565b813b156110c2576110c283838360405180602001604052805f81525061205a565b611272611f16565b610ece8282611e27565b600161128781611ce2565b506004805460ff60e01b1916600160e01b179055565b5f806112a883610e6e565b90506001600160a01b0381166112c057505f92915050565b670de0b6b3a76400006112e26ec26fabfe894d13233d5ec73f61cc72836120db565b026112fc6eca73a6df4c58b84c5b4b847fe8ff39836120db565b039392505050565b600161130f81611ce2565b60055460ff161561133357604051636752fae160e11b815260040160405180910390fd5b6005545f9061134f90600160481b900463ffffffff16426129bd565b6005805463ffffffff8084166101000264ffffffffff19909216919091176001179182905591925061138a91600160681b90910416826129bd565b6005805468ffffffff00000000001916600160281b63ffffffff939093169290920291909117905550506004805461ffff60e01b191661010160e01b179055565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2565b638000000061141281611ce2565b600361141f838583612a45565b50505050565b638000000061143381611ce2565b6001611440858783612a45565b50600261144e838583612a45565b505050505050565b5f61146033611049565b905090565b5f61146f82612033565b9050806114835763ceea21b65f526004601cfd5b919050565b5f8161149b57638f4eb6045f526004601cfd5b673ec412a9852d173d60c11b601c52815f5263ffffffff601c600c2054169050919050565b60016114cb81611ce2565b600454600160f01b900460ff16156114f6576040516303cb96db60e21b815260040160405180910390fd5b63ffffffff8216158061151257506301e1338063ffffffff8316115b1561153057604051632b39f6cb60e01b815260040160405180910390fd5b506005805463ffffffff909216600160881b0263ffffffff60881b19909216919091179055565b61155f611f16565b6115685f612105565b565b5f8061157583610e6e565b90506001600160a01b03811661158d57505f92915050565b6115bb6115a96eca73a6df4c58b84c5b4b847fe8ff39836120db565b6115b285611b0b565b80821191030290565b9392505050565b5f610e786115cf83612033565b611cc2565b606060028054610ded9061298b565b60016115ee81611ce2565b600454600160e81b900460ff1615611619576040516303cb96db60e21b815260040160405180910390fd5b63ffffffff8216158061163557506301e1338063ffffffff8316115b1561165357604051632b39f6cb60e01b815260040160405180910390fd5b506005805463ffffffff909216600160681b0263ffffffff60681b19909216919091179055565b5f68929eee149b4bd212685c156116985763ab143c065f526004601cfd5b3068929eee149b4bd212685d6116ad33610e6e565b90506001600160a01b0381166116d65760405163425ef92760e01b815260040160405180910390fd5b6116f1816eca73a6df4c58b84c5b4b847fe8ff39858561214b565b6116f9611da0565b5f68929eee149b4bd212685d92915050565b5f6115bb338484610f6f565b5f610e783383611b66565b600161172d81611ce2565b50600480546001600160f81b0316600160f81b179055565b611750858585610eb5565b833b156117985761179885858585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061205a92505050565b5050505050565b5f806117aa83612024565b90505f6117b682612033565b9050836001600160a01b03166117cb82610e6e565b6001600160a01b0316036111c4579392505050565b6060611809825f818152673ec412a9852d173d60c11b601c52602090208101015460601b151590565b6118265760405163677510db60e11b815260040160405180910390fd5b5f546001600160a01b0316801561188a57604051915063c87b56dd5f52826020525f806024601c845afa61185c573d5f833e3d82fd5b60205f803e60205f51833e815160205f5101602084013e815160208301015f81526020810160405250611955565b600380546118979061298b565b159050611955576115bb600380546118ae9061298b565b80601f01602080910402602001604051908101604052809291908181526020018280546118da9061298b565b80156119255780601f106118fc57610100808354040283529160200191611925565b820191905f5260205f20905b81548152906001019060200180831161190857829003601f168201915b5050505050604051806040016040528060048152602001637b69647d60e01b8152506119508661216f565b6121b1565b50919050565b600161196681611ce2565b61196e611d13565b600160601b82106119ba5760405162461bcd60e51b81526020600482015260116024820152703030b6b7bab73a30103a37b7903134b39760791b60448201526064015b60405180910390fd5b506001600160a01b039091165f90815260066020526040902055565b5f6115bb338484611113565b60016119ed81611ce2565b6119f5611d13565b611a008360016129bd565b63ffffffff168263ffffffff161015611a5b5760405162461bcd60e51b815260206004820152601d60248201527f60656e6460206d757374206265203e3d20607374617274202b2031602e00000060448201526064016119b1565b506005805460ff9490941664ffffffffff199094169390931761010063ffffffff938416021768ffffffff00000000001916600160281b91909216021790556004805462ffffff60e01b19169055565b611ab3611f16565b63389a75e1600c52805f526020600c208054421115611ad957636f5e88185f526004601cfd5b5f905561102581612105565b611aed611f16565b8060601b611b0257637448fbae5f526004601cfd5b61102581612105565b6005545f9060ff161561148357506001600160a01b0381165f9081526006602052604090205460055463ffffffff600160281b82048116916101009004164281116111c4578082034280841190840302840204949350505050565b5f68929eee149b4bd212685c15611b845763ab143c065f526004601cfd5b3068929eee149b4bd212685d611b9983611e32565b9050611bb6816eca73a6df4c58b84c5b4b847fe8ff393385611f3c565b6116f983611f7d565b6001611bca81611ce2565b60055460ff16600114611bf057604051636752fae160e11b815260040160405180910390fd5b600554610100900463ffffffff164210611c1d576040516372de7acd60e01b815260040160405180910390fd5b6005805463ffffffff428181166101000264ffffffffff199093169290921760021792839055611c5592600160881b900416906129bd565b6005805463ffffffff92909216600160281b0268ffffffff000000000019909216919091179055506004805460ff60f01b1916600160f01b179055565b6380000000611ca081611ce2565b505f80546001600160a01b0319166001600160a01b0392909216919091179055565b673ec412a9852d173d60c11b601c9081525f91909152600c205460201c90565b638b78c6d81954331461102557638b78c6d8600c52335f52806020600c205416611025576382b429005f526004601cfd5b466142681480611d2557504662aa36a7145b6115685760405162461bcd60e51b815260206004820152602260248201527f4f6e6c79206f6e205365706f6c6961202f20486f6c65736b7920746573746e656044820152613a1760f11b60648201526084016119b1565b61141f84846323b872dd876001600160a01b0316866001600160a01b0316866122d1565b5f611daa33610e6e565b60055490915060ff16611de257611dd06eca73a6df4c58b84c5b4b847fe8ff39826120db565b335f9081526006602052604090205550565b5f611dec33611b0b565b905080611e086eca73a6df4c58b84c5b4b847fe8ff39846120db565b1015610ece57604051631909a90560e01b815260040160405180910390fd5b610ece82825f612338565b5f611e3c82611cc2565b90506001600160a01b03811615611e5257919050565b600454600160401b90046001600160a01b031680611e8357604051630873153160e11b815260040160405180910390fd5b600480546001600160401b038082166001011667ffffffffffffffff199091168117909155611eb384825f61238f565b611ec0825f463085612421565b9250826001600160a01b031681856001600160a01b03167f019b61d9140f8784f4eed1e66569335630452af2d974ceb13d3e706c0786729660405160405180910390a46111c484846001600160a01b0316612493565b638b78c6d819543314611568576382b429005f526004601cfd5b610ece82826001612338565b60405181606052846040528260601b602c526323b872dd60601b600c5260205f6064601c5f885af1611f70573d5f823e3d81fd5b5f60605260405250505050565b5f611f9f6eca73a6df4c58b84c5b4b847fe8ff39611f9a84610e6e565b6120db565b60055490915060ff161580611fc05750600554610100900463ffffffff1642105b8061200357506001600160a01b0382165f90815260066020526040902054811180156120035750638b78c6d819546001600160a01b0316336001600160a01b0316145b15610ece576001600160a01b03919091165f90815260066020526040902055565b5f6020608d5f843c50505f5190565b5f818152673ec412a9852d173d60c11b601c5260209020810101546001600160a01b031690565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a084015280156120a1578060c08401826020870160045afa505b60208360a48301601c86015f8a5af16120c2573d156120c2573d5f843e3d83fd5b508060e01b82511461144e5763d1a57ed65f526004601cfd5b5f816014526370a0823160601b5f5260208060246010865afa601f3d111660205102905092915050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3811560ff1b8217905550565b61141f848463a9059cbb856001600160a01b0316856001600160a01b03165f6122d1565b60606080604051019050602081016040525f8152805f19835b928101926030600a8206018453600a900480612188575050819003601f19909101908152919050565b60608351835183516020870196506020860195506020850194506020604051019350828701838311612281576001838203015f602085106121f157508388205b601f851660200360031b89515b8b51818118831c6122685783156122385783888e201461223857808a5260019c8d019c90990198848d10612232575061227c565b506121fe565b5f5b8b8101518b82015260200187811061223a57509b87019b98860198871561226857848d10612232575061227c565b895260019b8c019b90980197838c106121fe575b505050505b84935060206040510194508781038585030192505b808810156122b1578751845260209788019790930192612296565b50505f818401908152602001604052601f19909201918252509392505050565b604051856014820152635194544760601b81525f6034820152608060548201525f60748201528460988201528360b88201528260d88201528160f8820152606460948201525f38610124601084015f8b5af161232f573d5f823e3d81fd5b50505050505050565b638b78c6d8600c52825f526020600c20805483811783612359575080841681185b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f80a3505050505050565b61239a5f84846124bf565b8260601b60601c9250815f52673ec412a9852d173d60c11b601c52828160a01b1760205f208301830155825f52601c600c20600181540163ffffffff811685026123f35767ea553b3401336cea851560021b526004601cfd5b905581835f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8138a4505050565b5f6040518660148201528560348201528460548201528360601b60601c6074820152826094820152638a54c52f60601b815260205f60a4601084015f6f6551c19487814612e58fe068137757585af1601f3d1116612486576320188a595f526004601cfd5b50505f5195945050505050565b673ec412a9852d173d60c11b601c52815f52601c600c2080548060201c831860201b8118825550505050565b6124c882611cc2565b6001600160e01b0316156124ef5760405163759be05b60e11b815260040160405180910390fd5b6001600160a01b038316156110c2575f818152673ec412a9852d173d60c11b601c5260208120820182015460a01c900361253c5760405163768a0e2b60e01b815260040160405180910390fd5b5f818152673ec412a9852d173d60c11b601c52602090208101810180546001600160a01b031981161890556125798261257485611cc2565b612493565b6110c2835f612493565b5f60208284031215612593575f80fd5b81356001600160e01b0319811681146115bb575f80fd5b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b80356001600160a01b0381168114611483575f80fd5b5f60208284031215612605575f80fd5b6115bb826125df565b5f6020828403121561261e575f80fd5b5035919050565b5f8060408385031215612636575f80fd5b61263f836125df565b946020939093013593505050565b803563ffffffff81168114611483575f80fd5b5f60208284031215612670575f80fd5b6115bb8261264d565b5f8083601f840112612689575f80fd5b5081356001600160401b0381111561269f575f80fd5b6020830191508360208260051b85010111156126b9575f80fd5b9250929050565b5f805f604084860312156126d2575f80fd5b6126db846125df565b925060208401356001600160401b038111156126f5575f80fd5b61270186828701612679565b9497909650939450505050565b5f805f60608486031215612720575f80fd5b612729846125df565b9250612737602085016125df565b9150604084013590509250925092565b5f8083601f840112612757575f80fd5b5081356001600160401b0381111561276d575f80fd5b6020830191508360208285010111156126b9575f80fd5b5f8060208385031215612795575f80fd5b82356001600160401b038111156127aa575f80fd5b6127b685828601612747565b90969095509350505050565b5f805f80604085870312156127d5575f80fd5b84356001600160401b03808211156127eb575f80fd5b6127f788838901612747565b9096509450602087013591508082111561280f575f80fd5b5061281c87828801612747565b95989497509550505050565b80358015158114611483575f80fd5b5f8060408385031215612848575f80fd5b612851836125df565b915061285f60208401612828565b90509250929050565b5f8060208385031215612879575f80fd5b82356001600160401b0381111561288e575f80fd5b6127b685828601612679565b5f602082840312156128aa575f80fd5b6115bb82612828565b5f805f805f608086880312156128c7575f80fd5b6128d0866125df565b94506128de602087016125df565b93506040860135925060608601356001600160401b038111156128ff575f80fd5b61290b88828901612747565b969995985093965092949392505050565b5f805f6060848603121561292e575f80fd5b833560ff8116811461293e575f80fd5b925061294c6020850161264d565b915061295a6040850161264d565b90509250925092565b5f8060408385031215612974575f80fd5b61297d836125df565b915061285f602084016125df565b600181811c9082168061299f57607f821691505b60208210810361195557634e487b7160e01b5f52602260045260245ffd5b63ffffffff8181168382160190808211156129e657634e487b7160e01b5f52601160045260245ffd5b5092915050565b634e487b7160e01b5f52604160045260245ffd5b601f8211156110c257805f5260205f20601f840160051c81016020851015612a265750805b601f840160051c820191505b81811015611798575f8155600101612a32565b6001600160401b03831115612a5c57612a5c6129ed565b612a7083612a6a835461298b565b83612a01565b5f601f841160018114612aa1575f8515612a8a5750838201355b5f19600387901b1c1916600186901b178355611798565b5f83815260208120601f198716915b82811015612ad05786850135825560209485019460019092019101612ab0565b5086821015612aec575f1960f88860031b161c19848701351681555b505060018560011b018355505050505056fea264697066735822122077798a98c56e954f07284ba2e0b0f9e9ae12504a117f14e1589d59f78e07566464736f6c63430008190033
Deployed Bytecode Sourcemap
163:12963:7:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14864:380:3;;;;;;;;;;-1:-1:-1;14864:380:3;;;;;:::i;:::-;15177:10;15042:3;15038:21;;;;15171:17;;;15196:10;15190:17;;15168:40;15216:10;15210:17;;;15165:63;;14864:380;;;;470:14:8;;463:22;445:41;;433:2;418:18;14864:380:3;;;;;;;;4010:98:6;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;20241:126::-;;;;;;;;;;-1:-1:-1;20241:126:6;;;;;:::i;:::-;;:::i;:::-;;;-1:-1:-1;;;;;1453:32:8;;;1435:51;;1423:2;1408:18;20241:126:6;1289:203:8;12609:119:7;;;;;;;;;;-1:-1:-1;12609:119:7;;;;;:::i;:::-;-1:-1:-1;12685:7:7;;12609:119;9265:321;;;;;;;;;;;;;:::i;:::-;;12435:120;;;;;;:::i;:::-;;:::i;6443:262::-;;;;;;;;;;-1:-1:-1;6443:262:7;;;;;:::i;:::-;;:::i;10759:489:6:-;;;;;;;;;;-1:-1:-1;10759:489:6;;;;;:::i;:::-;;:::i;2833:29:7:-;;;;;;;;;;-1:-1:-1;2833:29:7;;;;-1:-1:-1;;;2833:29:7;;;;;;;;;3360:10:8;3348:23;;;3330:42;;3318:2;3303:18;2833:29:7;3186:192:8;5772:96:6;;;;;;;;;;-1:-1:-1;5850:11:6;;-1:-1:-1;;;;;5850:11:6;5772:96;;;3529:25:8;;;3517:2;3502:18;5772:96:6;3383:177:8;10127:109:2;;;;;;:::i;:::-;;:::i;7239:130:7:-;;;;;;;;;;;;;:::i;7842:137:6:-;;;;;;;;;;-1:-1:-1;7842:137:6;;;;;:::i;:::-;;:::i;9588:123:2:-;;;;;;:::i;:::-;;:::i;11178:139::-;;;;;;;;;;-1:-1:-1;11178:139:2;;;;;:::i;:::-;10769:15;10763:4;10756:29;;;11257:4;10798:18;;;;10898:4;10882:21;;;10876:28;11280:21;;:30;;11178:139;6752:120:7;;;;;;;;;;;;;:::i;12247:134::-;;;;;;:::i;2615:26::-;;;;;;;;;;-1:-1:-1;2615:26:7;;;;;;;;;;;9021:617:1;;;:::i;9138:415:6:-;;;;;;;;;;-1:-1:-1;9138:415:6;;;;;:::i;:::-;;:::i;10567:353:2:-;;;;;;;;;;-1:-1:-1;10567:353:2;;;;;:::i;:::-;10769:15;10763:4;10756:29;;;10627:13;10798:18;;;;10898:4;10882:21;;10876:28;;10567:353;1064:55:6;;;;;;;;;;;;14166:7:2;1064:55:6;;2278:33:7;;;;;;;;;;-1:-1:-1;2278:33:7;;;;-1:-1:-1;;;2278:33:7;;;;;;21010:407:6;;;;;;;;;;-1:-1:-1;21010:407:6;;;;;:::i;:::-;;:::i;6075:164:7:-;;;;;;;;;;-1:-1:-1;6075:164:7;;;;;:::i;:::-;;:::i;10512:152:6:-;;;;;;;;;;-1:-1:-1;10512:152:6;;;;;:::i;:::-;;:::i;13705:198:3:-;;;;;;:::i;:::-;;:::i;2187:31:7:-;;;;;;;;;;-1:-1:-1;2187:31:7;;;;-1:-1:-1;;;2187:31:7;;;;;;9856:125:2;;;;;;:::i;:::-;;:::i;6284:116:7:-;;;;;;;;;;;;;:::i;11786:359:6:-;;;;;;;;;;-1:-1:-1;11786:359:6;;;;;:::i;:::-;;:::i;10982:134:2:-;;;;;;;;;;-1:-1:-1;10982:134:2;;;;;:::i;:::-;10769:15;10763:4;10756:29;;;11060:4;10798:18;;;;10898:4;10882:21;;;10876:28;11083:21;:26;;;10982:134;7779:408:7;;;;;;;;;;;;;:::i;9720:456:1:-;;;:::i;6199:118:6:-;;;;;;;;;;-1:-1:-1;6199:118:6;;;;;:::i;:::-;;:::i;6564:197::-;;;;;;;;;;-1:-1:-1;6564:197:6;;;;;:::i;:::-;;:::i;8035:113::-;;;;;;;;;;;;;:::i;2377:38:7:-;;;;;;;;;;-1:-1:-1;2377:38:7;;;;-1:-1:-1;;;2377:38:7;;;;;;6957:332:3;;;;;;;;;;-1:-1:-1;6957:332:3;;;;;:::i;:::-;;:::i;7433:533::-;;;;;;;;;;-1:-1:-1;7433:533:3;;;;;:::i;:::-;;:::i;7421:283:7:-;;;;;;;;;;-1:-1:-1;7421:283:7;;;;;:::i;:::-;;:::i;8762:100:1:-;;;:::i;520:44:7:-;;;;;;;;;;;;12635:6:2;520:44:7;;2910:34;;;;;;;;;;-1:-1:-1;2910:34:7;;;;-1:-1:-1;;;2910:34:7;;;;;;2692:24;;;;;;;;;;-1:-1:-1;2692:24:7;;;;-1:-1:-1;;;2692:24:7;;;;;;4115:399;;;;;;;;;;-1:-1:-1;4115:399:7;;;;;:::i;:::-;;:::i;2104:25::-;;;;;;;;;;-1:-1:-1;2104:25:7;;;;-1:-1:-1;;;2104:25:7;;-1:-1:-1;;;;;2104:25:7;;;21536:140:6;;;;;;;;;;-1:-1:-1;21536:140:6;;;;;:::i;:::-;;:::i;3183:25::-;;;;;;;;;;-1:-1:-1;3183:25:6;;;;-1:-1:-1;;;;;3183:25:6;;;;;;-1:-1:-1;;;;;5555:31:8;;;5537:50;;5525:2;5510:18;3183:25:6;5393:200:8;11408:182:1;;;;;;;;;;-1:-1:-1;;;11556:18:1;11408:182;;790:76:6;;;;;;;;;;;;824:42;790:76;;924:83;;;;;;;;;;;;965:42;924:83;;4142:102;;;;;;;;;;;;;:::i;6918:268:7:-;;;;;;;;;;-1:-1:-1;6918:268:7;;;;;:::i;:::-;;:::i;10032:382:6:-;;;;;;;;;;-1:-1:-1;10032:382:6;;;;;:::i;:::-;;:::i;12782:119:7:-;;;;;;;;;;-1:-1:-1;12782:119:7;;;;;:::i;21760:134:6:-;;;;;;;;;;-1:-1:-1;21760:134:6;;;;;:::i;:::-;21837:4;18323:16:3;;;-1:-1:-1;;;18359:4:3;18352:38;18460:4;18444:21;;18436:30;;18428:39;18422:46;18417:3;18413:56;21860:27:6;;;21760:134;2472:28:7;;;;;;;;;;-1:-1:-1;2472:28:7;;;;-1:-1:-1;;;2472:28:7;;;;;;2538:24;;;;;;;;;;-1:-1:-1;2538:24:7;;;;;;;;;;;6194:4:8;6182:17;;;6164:36;;6152:2;6137:18;2538:24:7;6022:184:8;11349:209:6;;;;;;;;;;-1:-1:-1;11349:209:6;;;;;:::i;:::-;;:::i;12070:123:7:-;;;;;;;;;;-1:-1:-1;12070:123:7;;;;;:::i;3032:66::-;;;;;;;;;;-1:-1:-1;3032:66:7;;;;;:::i;:::-;;;;;;;;;;;;;;8807:151:6;;;;;;;;;;-1:-1:-1;8807:151:6;;;;;:::i;:::-;;:::i;5924:110:7:-;;;;;;;;;;;;;:::i;14406:249:3:-;;;;;;:::i;:::-;;:::i;20488:411:6:-;;;;;;;;;;-1:-1:-1;20488:411:6;;;;;:::i;:::-;;:::i;4350:1382::-;;;;;;;;;;-1:-1:-1;4350:1382:6;;;;;:::i;:::-;;:::i;9661:290:7:-;;;;;;;;;;-1:-1:-1;9661:290:7;;;;;:::i;:::-;;:::i;9732:208:6:-;;;;;;;;;;-1:-1:-1;9732:208:6;;;;;:::i;:::-;;:::i;2756:27:7:-;;;;;;;;;;-1:-1:-1;2756:27:7;;;;-1:-1:-1;;;2756:27:7;;;;;;8753:437;;;;;;;;;;-1:-1:-1;8753:437:7;;;;;:::i;:::-;;:::i;12955:169::-;;;;;;;;;;-1:-1:-1;12955:169:7;;;;;:::i;:::-;13085:4;12955:169;;;;;10363:708:1;;;;;;:::i;:::-;;:::i;8348:349::-;;;;;;:::i;:::-;;:::i;4588:825:7:-;;;;;;;;;;-1:-1:-1;4588:825:7;;;;;:::i;:::-;;:::i;8325:308:6:-;;;;;;;;;;-1:-1:-1;8325:308:6;;;;;:::i;:::-;;:::i;11693:435:1:-;;;;;;;;;;-1:-1:-1;11693:435:1;;;;;:::i;:::-;11963:19;11957:4;11950:33;;;11812:14;11996:26;;;;12106:4;12090:21;;12084:28;;11693:435;8239:439:7;;;;;;;;;;;;;:::i;6374:138:6:-;;;;;;;;;;-1:-1:-1;6374:138:6;;;;;:::i;:::-;;:::i;4010:98::-;4064:13;4096:5;4089:12;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4010:98;:::o;20241:126::-;20302:7;20344:14;20352:5;20344:7;:14::i;:::-;20321:39;20241:126;-1:-1:-1;;20241:126:6:o;9265:321:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;9346:21:7::1;:19;:21::i;:::-;-1:-1:-1::0;9377:11:7::1;:32:::0;;-1:-1:-1;;9445:14:7;;;9469:19:::1;:27:::0;;-1:-1:-1;;;;9545:34:7;;;9265:321::o;12435:120::-;12537:11;;-1:-1:-1;;;12537:11:7;;;;;;;;;;;12520:28;12435:120;;:::o;6443:262::-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;6541:19:7::1;::::0;-1:-1:-1;;;6541:19:7;::::1;;;6537:40;;;6569:8;;-1:-1:-1::0;;;6569:8:7::1;;;;;;;;;;;6537:40;6591:10;::::0;::::1;::::0;;:43:::1;;-1:-1:-1::0;946:8:7::1;6605:29;::::0;::::1;;6591:43;6587:80;;;6643:24;;-1:-1:-1::0;;;6643:24:7::1;;;;;;;;;;;6587:80;-1:-1:-1::0;6677:13:7::1;:21:::0;;::::1;::::0;;::::1;-1:-1:-1::0;;;6677:21:7::1;-1:-1:-1::0;;6677:21:7;;::::1;::::0;;;::::1;::::0;;6443:262::o;10759:489:6:-;10908:13;1706:22:5;1700:29;1697:143;;;1761:10;1755:4;1748:24;1821:4;1815;1808:18;1697:143;1884:9;1860:22;1853:41;10945:19:6::1;10953:10;10945:7;:19::i;:::-;10937:27:::0;-1:-1:-1;;;;;;11004:19:6;::::1;11000:56;;11032:24;;-1:-1:-1::0;;;11032:24:6::1;;;;;;;;;;;11000:56;11071:9;11066:143;11082:19:::0;;::::1;11066:143;;;11122:76;11153:5:::0;965:42:::1;11176:2:::0;24939:1;24935:9;;;24921:24;;24908:38;11122:30:::1;:76::i;:::-;11103:3;;11066:143;;;;11218:23;:21;:23::i;:::-;2021:1:5::0;1997:22;1990:33;10759:489:6;;;;;:::o;10127:109:2:-;10198:31;10211:10;10223:5;10198:12;:31::i;:::-;10127:109;:::o;7239:130:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;-1:-1:-1;7329:26:7::1;:33:::0;;-1:-1:-1;;;;7329:33:7::1;-1:-1:-1::0;;;7329:33:7::1;::::0;;7239:130::o;7842:137:6:-;7919:13;1706:22:5;1700:29;1697:143;;;1761:10;1755:4;1748:24;1821:4;1815;1808:18;1697:143;1884:9;1860:22;1853:41;7952:20:6::1;7965:6;7952:12;:20::i;:::-;7944:28;;2021:1:5::0;1997:22;1990:33;7842:137:6;;;:::o;9588:123:2:-;12517:13:1;:11;:13::i;:::-;9680:24:2::1;9692:4;9698:5;9680:11;:24::i;6752:120:7:-:0;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;-1:-1:-1;6837:21:7::1;:28:::0;;-1:-1:-1;;;;6837:28:7::1;-1:-1:-1::0;;;6837:28:7::1;::::0;;6752:120::o;12346:28::-;12247:134;;;:::o;9021:617:1:-;9114:15;7972:9;-1:-1:-1;;;;;9132:46:1;:15;:46;9114:64;;9346:19;9340:4;9333:33;9396:8;9390:4;9383:22;9452:7;9445:4;9439;9429:21;9422:38;9599:8;9552:45;9549:1;9546;9541:67;9248:374;9021:617::o;9138:415:6:-;9291:13;1706:22:5;1700:29;1697:143;;;1761:10;1755:4;1748:24;1821:4;1815;1808:18;1697:143;1884:9;1860:22;1853:41;9328:20:6::1;9341:6;9328:12;:20::i;:::-;9320:28;;9363:9;9358:148;9374:19:::0;;::::1;9358:148;;;9414:81;9442:5:::0;965:42:::1;9465:10;24939:1:::0;24935:9;;;24921:24;;24908:38;9414:27:::1;:81::i;:::-;9395:3;;9358:148;;;;9515:31;9539:6;9515:23;:31::i;21010:407::-:0;21076:14;21165:15;21183:25;21202:5;21183:18;:25::i;:::-;21165:43;;21218:21;21242:17;21251:7;21242:8;:17::i;:::-;21218:41;;21387:5;-1:-1:-1;;;;;21361:31:6;:22;21369:13;21361:7;:22::i;:::-;-1:-1:-1;;;;;21361:31:6;;21357:53;;21403:7;21394:16;;21357:53;21092:325;;21010:407;;;:::o;6075:164:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;6171:16:7::1;::::0;-1:-1:-1;;;6171:16:7;::::1;;;6167:37;;;6196:8;;-1:-1:-1::0;;;6196:8:7::1;;;;;;;;;;;6167:37;-1:-1:-1::0;6214:10:7::1;:18:::0;;-1:-1:-1;;;;;6214:18:7;;::::1;-1:-1:-1::0;;;6214:18:7::1;-1:-1:-1::0;;;;;;6214:18:7;;::::1;::::0;;;::::1;::::0;;6075:164::o;10512:152:6:-;10582:13;10615:42;10638:10;10650:6;10615:22;:42::i;13705:198:3:-;13802:26;13815:4;13821:2;13825;13802:12;:26::i;:::-;38367:14;;13838:58;;;13856:40;13879:4;13885:2;13889;13856:40;;;;;;;;;;;;:22;:40::i;9856:125:2:-;12517:13:1;:11;:13::i;:::-;9949:25:2::1;9962:4;9968:5;9949:12;:25::i;6284:116:7:-:0;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;-1:-1:-1;6367:19:7::1;:26:::0;;-1:-1:-1;;;;6367:26:7::1;-1:-1:-1::0;;;6367:26:7::1;::::0;;6284:116::o;11786:359:6:-;11921:7;11944:13;11960:15;11968:6;11960:7;:15::i;:::-;11944:31;-1:-1:-1;;;;;;11989:19:6;;11985:33;;-1:-1:-1;12017:1:6;;11786:359;-1:-1:-1;;11786:359:6:o;11985:33::-;1207:8;12088:33;965:42;12115:5;12088:10;:33::i;:::-;:40;12059:26;824:42;12079:5;12059:10;:26::i;:::-;:69;;11786:359;-1:-1:-1;;;11786:359:6:o;7779:408:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;7859:11:7::1;::::0;:33:::1;:11;:33:::0;7855:66:::1;;7901:20;;-1:-1:-1::0;;;7901:20:7::1;;;;;;;;;;;7855:66;7972:13;::::0;7931:12:::1;::::0;7946:39:::1;::::0;-1:-1:-1;;;7972:13:7;::::1;;;7953:15;7946:39;:::i;:::-;7995:11;:35:::0;;8040:20:::1;::::0;;::::1;7995:35;8040:20;-1:-1:-1::0;;8040:20:7;;;;;;;760:1:::1;8040:20:::0;;;;;7931:54;;-1:-1:-1;8083:23:7::1;::::0;-1:-1:-1;;;8091:15:7;;::::1;;7931:54:::0;8083:23:::1;:::i;:::-;8070:10;:36:::0;;-1:-1:-1;;8070:36:7::1;-1:-1:-1::0;;;8070:36:7::1;::::0;;;::::1;::::0;;;::::1;::::0;;;::::1;::::0;;-1:-1:-1;;8116:19:7::1;:26:::0;;-1:-1:-1;;;;8152:28:7;-1:-1:-1;;;8152:28:7;;;7779:408::o;9720:456:1:-;9922:19;9916:4;9909:33;9968:8;9962:4;9955:22;10020:1;10013:4;10007;9997:21;9990:32;10151:8;10105:44;10102:1;10099;10094:66;9720:456::o;6199:118:6:-;14166:7:2;11991:25;12010:5;11991:18;:25::i;:::-;6296:8:6::1;:14;6307:3:::0;;6296:8;:14:::1;:::i;:::-;;6199:118:::0;;;:::o;6564:197::-;14166:7:2;11991:25;12010:5;11991:18;:25::i;:::-;6714:5:6::1;:13;6722:5:::0;;6714;:13:::1;:::i;:::-;-1:-1:-1::0;6737:7:6::1;:17;6747:7:::0;;6737;:17:::1;:::i;:::-;;6564:197:::0;;;;;:::o;8035:113::-;8082:13;8115:26;8130:10;8115:14;:26::i;:::-;8107:34;;8035:113;:::o;6957:332:3:-;7015:14;7050:12;7059:2;7050:8;:12::i;:::-;7041:21;;7148:6;7138:135;;7187:10;7181:4;7174:24;7254:4;7248;7241:18;7138:135;6957:332;;;:::o;7433:533::-;7496:14;7656:5;7646:143;;7694:10;7688:4;7681:24;7770:4;7764;7757:18;7646:143;-1:-1:-1;;;7809:4:3;7802:38;7866:5;7860:4;7853:19;7929:20;7921:4;7915;7905:21;7899:28;7895:55;7885:65;;7433:533;;;:::o;7421:283:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;7526:26:7::1;::::0;-1:-1:-1;;;7526:26:7;::::1;;;7522:47;;;7561:8;;-1:-1:-1::0;;;7561:8:7::1;;;;;;;;;;;7522:47;7583:10;::::0;::::1;::::0;;:43:::1;;-1:-1:-1::0;946:8:7::1;7597:29;::::0;::::1;;7583:43;7579:80;;;7635:24;;-1:-1:-1::0;;;7635:24:7::1;;;;;;;;;;;7579:80;-1:-1:-1::0;7669:20:7::1;:28:::0;;::::1;::::0;;::::1;-1:-1:-1::0;;;7669:28:7::1;-1:-1:-1::0;;;;7669:28:7;;::::1;::::0;;;::::1;::::0;;7421:283::o;8762:100:1:-;12517:13;:11;:13::i;:::-;8834:21:::1;8852:1;8834:9;:21::i;:::-;8762:100::o:0;4115:399:7:-;4226:14;4256:13;4272:15;4280:6;4272:7;:15::i;:::-;4256:31;-1:-1:-1;;;;;;4301:19:7;;4297:33;;-1:-1:-1;4329:1:7;;4115:399;-1:-1:-1;;4115:399:7:o;4297:33::-;4429:78;4443:26;824:42:6;4463:5:7;4443:10;:26::i;:::-;4471:35;4499:6;4471:27;:35::i;:::-;24414:8:6;;;24424:9;;24410:24;;24250:200;4429:78:7;4422:85;4115:399;-1:-1:-1;;;4115:399:7:o;21536:140:6:-;21599:7;21641:26;21649:17;21658:7;21649:8;:17::i;:::-;21641:7;:26::i;4142:102::-;4198:13;4230:7;4223:14;;;;;:::i;6918:268:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;7018:21:7::1;::::0;-1:-1:-1;;;7018:21:7;::::1;;;7014:42;;;7048:8;;-1:-1:-1::0;;;7048:8:7::1;;;;;;;;;;;7014:42;7070:10;::::0;::::1;::::0;;:43:::1;;-1:-1:-1::0;946:8:7::1;7084:29;::::0;::::1;;7070:43;7066:80;;;7122:24;;-1:-1:-1::0;;;7122:24:7::1;;;;;;;;;;;7066:80;-1:-1:-1::0;7156:15:7::1;:23:::0;;::::1;::::0;;::::1;-1:-1:-1::0;;;7156:23:7::1;-1:-1:-1::0;;;;7156:23:7;;::::1;::::0;;;::::1;::::0;;6918:268::o;10032:382:6:-;10161:13;1706:22:5;1700:29;1697:143;;;1761:10;1755:4;1748:24;1821:4;1815;1808:18;1697:143;1884:9;1860:22;1853:41;10198:19:6::1;10206:10;10198:7;:19::i;:::-;10190:27:::0;-1:-1:-1;;;;;;10257:19:6;::::1;10253:56;;10285:24;;-1:-1:-1::0;;;10285:24:6::1;;;;;;;;;;;10253:56;10319:55;10347:5;824:42;10363:2;10367:6;10319:27;:55::i;:::-;10384:23;:21;:23::i;:::-;2021:1:5::0;1997:22;1990:33;10032:382:6;;;;:::o;11349:209::-;11463:13;11500:51;11530:10;11542:8;;11500:29;:51::i;8807:151::-;8876:13;8909:42;8932:10;8944:6;8909:22;:42::i;5924:110:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;-1:-1:-1;6004:16:7::1;:23:::0;;-1:-1:-1;;;;;6004:23:7::1;-1:-1:-1::0;;;6004:23:7::1;::::0;;5924:110::o;14406:249:3:-;14552:26;14565:4;14571:2;14575;14552:12;:26::i;:::-;38367:14;;14588:60;;;14606:42;14629:4;14635:2;14639;14643:4;;14606:42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;14606:22:3;;-1:-1:-1;;;14606:42:3:i;:::-;14406:249;;;;;:::o;20488:411:6:-;20552:14;20641:15;20659:25;20678:5;20659:18;:25::i;:::-;20641:43;;20694:21;20718:17;20727:7;20718:8;:17::i;:::-;20694:41;;20863:5;-1:-1:-1;;;;;20837:31:6;:22;20845:13;20837:7;:22::i;:::-;-1:-1:-1;;;;;20837:31:6;;20833:59;;20879:13;20488:411;-1:-1:-1;;;20488:411:6:o;4350:1382::-;4418:20;4455:11;4463:2;15636:11:3;15725:16;;;-1:-1:-1;;;15761:4:3;15754:38;15875:4;15859:21;;15851:30;;15843:39;15837:46;15833:2;15829:55;15822:63;15815:71;;15576:326;4455:11:6;4450:44;;4475:19;;-1:-1:-1;;;4475:19:6;;;;;;;;;;;4450:44;4505:16;4524:17;-1:-1:-1;;;;;4524:17:6;4623:22;;4619:1107;;4751:4;4745:11;4735:21;;4786:10;4780:4;4773:24;4851:2;4845:4;4838:16;4927:4;4921;4915;4909;4899:8;4892:5;4881:51;4871:202;;4985:16;4979:4;4971:6;4956:46;5038:16;5030:6;5023:32;4871:202;5117:4;5111;5105;5090:32;5223:4;5216;5210:11;5202:6;5187:41;5343:6;5337:13;5330:4;5323;5317:11;5313:22;5306:4;5298:6;5294:17;5279:72;5428:6;5422:13;5415:4;5407:6;5403:17;5399:37;5465:1;5460:3;5453:14;5544:4;5539:3;5535:14;5529:4;5522:28;;4619:1107;;;5610:8;5604:22;;;;;:::i;:::-;:27;;-1:-1:-1;5600:126:6;;5656:59;5674:8;5656:59;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5656:59:6;;;5692:22;5711:2;5692:18;:22::i;:::-;5656:17;:59::i;5600:126::-;4440:1292;4350:1382;;;:::o;9661:290:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;9809:21:7::1;:19;:21::i;:::-;-1:-1:-1::0;;;9848:6:7::1;:16;9840:46;;;::::0;-1:-1:-1;;;9840:46:7;;11169:2:8;9840:46:7::1;::::0;::::1;11151:21:8::0;11208:2;11188:18;;;11181:30;-1:-1:-1;;;11227:18:8;;;11220:47;11284:18;;9840:46:7::1;;;;;;;;;-1:-1:-1::0;;;;;;9896:39:7;;::::1;;::::0;;;:31:::1;:39;::::0;;;;:48;9661:290::o;9732:208:6:-;9845:13;9882:51;9912:10;9924:8;;9882:29;:51::i;8753:437:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;8890:21:7::1;:19;:21::i;:::-;8936:9;:5:::0;8944:1:::1;8936:9;:::i;:::-;8929:16;;:3;:16;;;;8921:58;;;::::0;-1:-1:-1;;;8921:58:7;;11515:2:8;8921:58:7::1;::::0;::::1;11497:21:8::0;11554:2;11534:18;;;11527:30;11593:31;11573:18;;;11566:59;11642:18;;8921:58:7::1;11313:353:8::0;8921:58:7::1;-1:-1:-1::0;8989:11:7::1;:18:::0;;::::1;::::0;;;::::1;-1:-1:-1::0;;9017:20:7;;;;;;;8989:18:::1;9017:20;::::0;;::::1;;;-1:-1:-1::0;;9047:16:7::1;-1:-1:-1::0;;;9047:16:7;;;::::1;;;::::0;;9073:19:::1;:27:::0;;-1:-1:-1;;;;9149:34:7;;;8753:437::o;10363:708:1:-;12517:13;:11;:13::i;:::-;10597:19:::1;10591:4;10584:33;10643:12;10637:4;10630:26;10705:4;10699;10689:21;10811:12;10805:19;10792:11;10789:36;10786:157;;;10857:10;10851:4;10844:24;10924:4;10918;10911:18;10786:157;11020:1;10999:23:::0;;11041::::1;11051:12:::0;11041:9:::1;:23::i;8348:349::-:0;12517:13;:11;:13::i;:::-;8520:8:::1;8516:2;8512:17;8502:150;;8562:10;8556:4;8549:24;8633:4;8627;8620:18;8502:150;8671:19;8681:8;8671:9;:19::i;4588:825:7:-:0;4912:11;;4702:14;;4912:33;:11;:33;4908:499;;-1:-1:-1;;;;;;4970:39:7;;;;;;:31;:39;;;;;;5055:10;;;-1:-1:-1;;;5055:10:7;;;;;;5095:12;;;5125:15;:24;-1:-1:-1;5121:276:7;;5352:11;;;5325:15;24414:8:6;;;24424:9;;;24410:24;5306:44:7;;24653:9:6;5289:75:7;4588:825;-1:-1:-1;;;;4588:825:7:o;8325:308:6:-;8458:13;1706:22:5;1700:29;1697:143;;;1761:10;1755:4;1748:24;1821:4;1815;1808:18;1697:143;1884:9;1860:22;1853:41;8495:20:6::1;8508:6;8495:12;:20::i;:::-;8487:28;;8525:60;8550:5;824:42;8566:10;8578:6;8525:24;:60::i;:::-;8595:31;8619:6;8595:23;:31::i;8239:439:7:-:0;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;8326:11:7::1;::::0;:36:::1;:11;760:1;8326:36;8322:69;;8371:20;;-1:-1:-1::0;;;8371:20:7::1;;;;;;;;;;;8322:69;8424:12;::::0;::::1;::::0;::::1;;;8405:15;:31;8401:67;;8445:23;;-1:-1:-1::0;;;8445:23:7::1;;;;;;;;;;;8401:67;8478:11;:33:::0;;8521:38:::1;8543:15;8521:38:::0;;::::1;8478:33;8521:38;-1:-1:-1::0;;8521:38:7;;;;;;;845:1:::1;8521:38:::0;;;;;8582:46:::1;::::0;-1:-1:-1;;;8608:20:7;::::1;;::::0;8582:46:::1;:::i;:::-;8569:10;:59:::0;;::::1;::::0;;;::::1;-1:-1:-1::0;;;8569:59:7::1;-1:-1:-1::0;;8569:59:7;;::::1;::::0;;;::::1;::::0;;-1:-1:-1;8638:26:7::1;:33:::0;;-1:-1:-1;;;;8638:33:7::1;-1:-1:-1::0;;;8638:33:7::1;::::0;;8239:439::o;6374:138:6:-;14166:7:2;11991:25;12010:5;11991:18;:25::i;:::-;-1:-1:-1;6477:17:6::1;:28:::0;;-1:-1:-1;;;;;;6477:28:6::1;-1:-1:-1::0;;;;;6477:28:6;;;::::1;::::0;;;::::1;::::0;;6374:138::o;17011:301:3:-;-1:-1:-1;;;17173:4:3;17166:38;;;17074:14;17217:19;;;;17283:4;17273:21;17267:28;17263:2;17259:37;;17011:301::o;5219:802:2:-;-1:-1:-1;;5507:27:2;5497:8;5494:41;5484:521;;5610:15;5604:4;5597:29;5656:8;5650:4;5643:22;5859:5;5851:4;5845;5835:21;5829:28;5825:40;5815:176;;5902:10;5896:4;5889:24;5968:4;5962;5955:18;10025:193:7;10100:13;10117:5;10100:22;:51;;;;10126:13;10143:8;10126:25;10100:51;10079:132;;;;-1:-1:-1;;;10079:132:7;;11873:2:8;10079:132:7;;;11855:21:8;11912:2;11892:18;;;11885:30;11951:34;11931:18;;;11924:62;-1:-1:-1;;;12002:18:8;;;11995:32;12044:19;;10079:132:7;11671:398:8;17773:309:6;17996:79;18005:5;18012:12;18026:10;18046:5;-1:-1:-1;;;;;17996:79:6;18062:2;-1:-1:-1;;;;;17996:79:6;18067:7;17996:8;:79::i;11379:637:7:-;11462:13;11478:19;11486:10;11478:7;:19::i;:::-;11511:11;;11462:35;;-1:-1:-1;11511:33:7;:11;11507:503;;11791:26;824:42:6;11811:5:7;11791:10;:26::i;:::-;11777:10;11745:43;;;;:31;:43;;;;;:72;10127:109:2;:::o;11507:503:7:-;11848:23;11874:39;11902:10;11874:27;:39::i;:::-;11848:65;;11960:15;11931:26;824:42:6;11951:5:7;11931:10;:26::i;:::-;:44;11927:72;;;11984:15;;-1:-1:-1;;;11984:15:7;;;;;;;;;;;4304:117:2;4382:32;4395:4;4401:5;4408;4382:12;:32::i;12689:1254:6:-;12752:13;12801:14;12809:5;12801:7;:14::i;:::-;12777:40;-1:-1:-1;;;;;;12831:19:6;;;12827:37;;12689:1254;;;:::o;12827:37::-;5597:10:7;;-1:-1:-1;;;5597:10:7;;-1:-1:-1;;;;;5597:10:7;;12941:57:6;;12973:25;;-1:-1:-1;;;12973:25:6;;;;;;;;;;;12941:57;13032:11;13030:13;;-1:-1:-1;;;;;13030:13:6;;;;;;-1:-1:-1;;13030:13:6;;;;;;;;13095:48;13125:5;13030:13;13012:15;13095:29;:48::i;:::-;13402:334;13444:5;13545:1;13605:13;13658:4;13701:7;13402:24;:334::i;:::-;13394:342;;13784:5;-1:-1:-1;;;;;13755:35:6;13775:7;13768:5;-1:-1:-1;;;;;13755:35:6;;;;;;;;;;;13896:30;13904:5;13919;-1:-1:-1;;;;;13896:30:6;:7;:30::i;7292:355:1:-;-1:-1:-1;;7498:18:1;7488:8;7485:32;7475:156;;7550:10;7544:4;7537:24;7612:4;7606;7599:18;4053:115:2;4130:31;4143:4;4149:5;4156:4;4130:12;:31::i;16623:989:6:-;16874:4;16868:11;16939:7;16933:4;16926:21;17006:5;17000:4;16993:19;17074:4;17070:2;17066:13;17060:4;17053:27;-1:-1:-1;;;17130:4:6;17123:48;17346:4;17340;17334;17328;17325:1;17311:12;17304:5;17299:52;17289:181;;17395:16;17389:4;17386:1;17371:41;17439:16;17436:1;17429:27;17289:181;17496:1;17490:4;17483:15;17552:4;17545:15;-1:-1:-1;;;;16623:989:6:o;10553:629:7:-;10652:22;10677:36;824:42:6;10697:15:7;10705:6;10697:7;:15::i;:::-;10677:10;:36::i;:::-;10914:11;;10652:61;;-1:-1:-1;10914:33:7;:11;:33;;:67;;-1:-1:-1;10969:12:7;;;;;;;10951:15;:30;10914:67;:170;;;-1:-1:-1;;;;;;11019:39:7;;;;;;:31;:39;;;;;;11002:56;;:81;;;;-1:-1:-1;;;11556:18:1;-1:-1:-1;;;;;11062:21:7;:10;-1:-1:-1;;;;;11062:21:7;;11002:81;10897:279;;;-1:-1:-1;;;;;11109:39:7;;;;;;;;:31;:39;;;;;:56;10553:629::o;10486:225:0:-;10537:14;10656:4;10650;10644;10641:1;10629:32;-1:-1:-1;;10690:4:0;10684:11;;10486:225::o;16037:323:3:-;16098:14;16190:16;;;-1:-1:-1;;;16226:4:3;16219:38;16334:4;16318:21;;16310:30;;16302:39;16296:46;-1:-1:-1;;;;;16280:64:3;;16037:323::o;38588:1370::-;38825:4;38819:11;38875:10;38908:24;38905:1;38898:35;38967:8;38960:4;38957:1;38953:12;38946:30;39075:4;39071:2;39067:13;39063:2;39059:22;39052:4;39049:1;39045:12;39038:44;39116:2;39109:4;39106:1;39102:12;39095:24;39153:4;39146;39143:1;39139:12;39132:26;39186:4;39180:11;39225:1;39218:4;39215:1;39211:12;39204:23;39243:1;39240:71;;;39306:1;39299:4;39296:1;39292:12;39289:1;39282:4;39276;39272:15;39269:1;39262:5;39251:57;39247:62;39240:71;39427:4;39424:1;39417:4;39414:1;39410:12;39403:4;39400:1;39396:12;39393:1;39389:2;39382:5;39377:55;39367:313;;39455:16;39452:214;;;39583:16;39577:4;39574:1;39559:41;39631:16;39628:1;39621:27;39452:214;39367:313;39776:24;39771:3;39767:34;39763:1;39757:8;39754:48;39744:198;;39835:10;39829:4;39822:24;39923:4;39917;39910:18;19383:740:6;19458:14;19563:7;19557:4;19550:21;-1:-1:-1;;;19624:4:6;19617:48;20062:4;20056;20050;20044;20037:5;20030;20019:48;19957:4;19939:16;19936:26;19847:242;19820:4;19814:11;19729:378;19703:404;;19383:740;;;;:::o;6145:1089:1:-;-1:-1:-1;;6579:16:1;;-1:-1:-1;;;;;6427:26:1;;;;;;6539:38;6536:1;;6528:78;6703:16;;6698:3;6694:26;6681:40;;6663:59;;10127:109:2;:::o;15928:282:6:-;16132:71;16141:5;16148:9;16159:10;16179:2;-1:-1:-1;;;;;16132:71:6;16192:6;-1:-1:-1;;;;;16132:71:6;16201:1;16132:8;:71::i;1946:1641:4:-;2002:17;2447:4;2440;2434:11;2430:22;2423:29;;2546:4;2541:3;2537:14;2531:4;2524:28;2627:1;2622:3;2615:14;2728:3;2758:1;2754:6;2967:5;2949:402;3005:11;;;;3186:2;3200;3190:13;;3182:22;3005:11;3169:36;3292:2;3282:13;;3312:25;2949:402;3312:25;-1:-1:-1;;3379:13:4;;;-1:-1:-1;;3492:14:4;;;3552:19;;;3492:14;1946:1641;-1:-1:-1;1946:1641:4:o;19186:3338::-;19322:20;19451:7;19445:14;19498:6;19492:13;19549:11;19543:18;19599:4;19590:7;19586:18;19575:29;;19639:4;19631:6;19627:17;19617:27;;19689:4;19676:11;19672:22;19657:37;;19734:4;19727;19721:11;19717:22;19707:32;;19784:13;19775:7;19771:27;19838:13;19824:12;19821:31;19811:1966;;19931:1;19916:12;19904:10;19900:29;19896:37;19959:1;20004:4;19990:12;19987:22;19977:74;;-1:-1:-1;20018:31:4;;;19977:74;20112:4;20098:12;20094:23;20088:4;20084:34;20081:1;20077:42;20151:6;20145:13;20175:1588;20224:7;20218:14;20402:1;20399;20395:9;20392:1;20388:17;20378:1166;;20436:1;20433:433;;;20515:1;20500:12;20491:7;20481:32;20478:39;20468:372;;20553:17;;;20625:1;20671:15;;;;20613:14;;;;20729:29;;;20719:50;;20762:5;;;20719:50;20802:8;20175:1588;;20468:372;20976:1;20961:250;21044:19;;;21038:26;21022:14;;;21015:50;21106:4;21099:12;21150:24;;;20961:250;21140:45;-1:-1:-1;21312:26:4;;;;21246:30;;;;21363:159;;;;21431:16;21422:7;21419:29;21409:50;;21452:5;;;21363:159;21565:17;;21625:1;21659:15;;;;21613:14;;;;21705:29;;;20175:1588;21695:50;20175:1588;20179:2;;;;19811:1966;21814:6;21791:29;;21860:4;21853;21847:11;21843:22;21833:32;;21937:7;21925:10;21921:24;21912:6;21895:15;21891:28;21887:59;21878:68;;22022:213;22041:10;22032:7;22029:23;22022:213;;;22098:14;;22074:39;;22170:4;22203:18;;;;22149:26;;;;22022:213;;;-1:-1:-1;;22304:17:4;22300:25;;;22376:15;;;22270:4;22417:15;22411:4;22404:29;-1:-1:-1;;22258:17:4;;;22470;;;-1:-1:-1;22258:17:4;19186:3338;-1:-1:-1;;;19186:3338:4:o;18229:1017:6:-;18502:4;18496:11;18541:6;18534:4;18531:1;18527:12;18520:28;-1:-1:-1;;;18568:1:6;18561:45;18683:1;18676:4;18673:1;18669:12;18662:23;18731:4;18724;18721:1;18717:12;18710:26;18791:1;18784:4;18781:1;18777:12;18770:23;18843:10;18836:4;18833:1;18829:12;18822:32;18888:4;18881;18878:1;18874:12;18867:26;18927:4;18920;18917:1;18913:12;18906:26;18966:4;18959;18956:1;18952:12;18945:26;19005:4;18998;18995:1;18991:12;18984:26;19106:4;19094:10;19087:5;19080:4;19077:1;19073:12;19070:1;19063:5;19056;19051:60;19041:189;;19155:16;19149:4;19146:1;19131:41;19199:16;19196:1;19189:27;19041:189;;18229:1017;;;;;;:::o;2952:967:2:-;3118:15;3112:4;3105:29;3160:4;3154;3147:18;3210:4;3204;3194:21;3288:8;3282:15;3395:5;3386:7;3383:18;3639:2;3629:62;;-1:-1:-1;3669:19:2;;;3656:33;;3629:62;3763:7;3753:8;3746:25;3895:7;3887:4;3881:11;3877:2;3873:20;3841:30;3838:1;3835;3830:73;;;;2952:967;;;:::o;21573:1353:3:-;21677:40;21706:1;21710:2;21714;21677:20;:40::i;:::-;21855:2;21851;21847:11;21843:2;21839:20;21833:26;;21938:2;21932:4;21925:16;-1:-1:-1;;;21961:4:3;21954:38;22073:2;22065:5;22060:3;22056:15;22053:23;22044:4;22038;22028:21;22024:2;22020:30;22016:2;22012:39;22005:72;22172:2;22166:4;22159:16;22227:4;22221;22211:21;22298:1;22284:11;22278:18;22274:26;22450:20;22431:17;22427:44;22423:2;22419:53;22409:268;;22601:18;22595:2;22588:10;22585:1;22581:18;22574:46;22654:4;22648;22641:18;22409:268;22694:38;;22858:2;22854;22851:1;22824:25;22851:1;22806:10;22801:60;12247:134:7;;;:::o;4854:1046:0:-;5041:14;5148:4;5142:11;5187:15;5180:4;5177:1;5173:12;5166:37;5237:5;5230:4;5227:1;5223:12;5216:27;5277:8;5270:4;5267:1;5263:12;5256:30;5336:14;5332:2;5328:23;5324:2;5320:32;5313:4;5310:1;5306:12;5299:54;5387:8;5380:4;5377:1;5373:12;5366:30;-1:-1:-1;;;5489:1:0;5482:45;5691:4;5685;5679;5672;5669:1;5665:12;5662:1;5652:8;5645:5;5640:56;5613:4;5595:16;5592:26;5567:147;5540:310;;5760:10;5754:4;5747:24;5831:4;5825;5818:18;5540:310;-1:-1:-1;;5879:4:0;5873:11;;4854:1046;-1:-1:-1;;;;;4854:1046:0:o;17563:407:3:-;-1:-1:-1;;;17710:4:3;17703:38;17767:5;17761:4;17754:19;17821:4;17815;17805:21;17859:11;17853:18;17943:6;17939:2;17935:15;17928:5;17924:27;17920:2;17916:36;17908:6;17904:49;17891:11;17884:70;;;17563:407;;:::o;22976:554:6:-;23125:11;23133:2;23125:7;:11::i;:::-;-1:-1:-1;;;;;23125:16:6;;23121:53;;23150:24;;-1:-1:-1;;;23150:24:6;;;;;;;;;;;23121:53;-1:-1:-1;;;;;23188:18:6;;;23184:340;;18232:13:3;18323:16;;;-1:-1:-1;;;18359:4:3;18352:38;18460:4;18444:21;;18436:30;;18428:39;;18422:46;18417:3;18413:56;23226:27:6;;23222:66;;23262:26;;-1:-1:-1;;;23262:26:6;;;;;;;;;;;23222:66;23325:1;18831:16:3;;;-1:-1:-1;;;18867:4:3;18860:38;18964:4;18948:21;;18940:30;;18932:39;;18998:20;;-1:-1:-1;;;;;;19065:38:3;;19053:51;19031:74;;23385:26:6;23393:2;23397:13;23405:4;23397:7;:13::i;:::-;23385:7;:26::i;:::-;23460:16;23468:4;23474:1;23460:7;:16::i;14:286:8:-;72:6;125:2;113:9;104:7;100:23;96:32;93:52;;;141:1;138;131:12;93:52;167:23;;-1:-1:-1;;;;;;219:32:8;;209:43;;199:71;;266:1;263;256:12;497:418;646:2;635:9;628:21;609:4;678:6;672:13;721:6;716:2;705:9;701:18;694:34;780:6;775:2;767:6;763:15;758:2;747:9;743:18;737:50;836:1;831:2;822:6;811:9;807:22;803:31;796:42;906:2;899;895:7;890:2;882:6;878:15;874:29;863:9;859:45;855:54;847:62;;;497:418;;;;:::o;920:173::-;988:20;;-1:-1:-1;;;;;1037:31:8;;1027:42;;1017:70;;1083:1;1080;1073:12;1098:186;1157:6;1210:2;1198:9;1189:7;1185:23;1181:32;1178:52;;;1226:1;1223;1216:12;1178:52;1249:29;1268:9;1249:29;:::i;1497:180::-;1556:6;1609:2;1597:9;1588:7;1584:23;1580:32;1577:52;;;1625:1;1622;1615:12;1577:52;-1:-1:-1;1648:23:8;;1497:180;-1:-1:-1;1497:180:8:o;1682:254::-;1750:6;1758;1811:2;1799:9;1790:7;1786:23;1782:32;1779:52;;;1827:1;1824;1817:12;1779:52;1850:29;1869:9;1850:29;:::i;:::-;1840:39;1926:2;1911:18;;;;1898:32;;-1:-1:-1;;;1682:254:8:o;1941:163::-;2008:20;;2068:10;2057:22;;2047:33;;2037:61;;2094:1;2091;2084:12;2109:184;2167:6;2220:2;2208:9;2199:7;2195:23;2191:32;2188:52;;;2236:1;2233;2226:12;2188:52;2259:28;2277:9;2259:28;:::i;2298:367::-;2361:8;2371:6;2425:3;2418:4;2410:6;2406:17;2402:27;2392:55;;2443:1;2440;2433:12;2392:55;-1:-1:-1;2466:20:8;;-1:-1:-1;;;;;2498:30:8;;2495:50;;;2541:1;2538;2531:12;2495:50;2578:4;2570:6;2566:17;2554:29;;2638:3;2631:4;2621:6;2618:1;2614:14;2606:6;2602:27;2598:38;2595:47;2592:67;;;2655:1;2652;2645:12;2592:67;2298:367;;;;;:::o;2670:511::-;2765:6;2773;2781;2834:2;2822:9;2813:7;2809:23;2805:32;2802:52;;;2850:1;2847;2840:12;2802:52;2873:29;2892:9;2873:29;:::i;:::-;2863:39;;2953:2;2942:9;2938:18;2925:32;-1:-1:-1;;;;;2972:6:8;2969:30;2966:50;;;3012:1;3009;3002:12;2966:50;3051:70;3113:7;3104:6;3093:9;3089:22;3051:70;:::i;:::-;2670:511;;3140:8;;-1:-1:-1;3025:96:8;;-1:-1:-1;;;;2670:511:8:o;3565:328::-;3642:6;3650;3658;3711:2;3699:9;3690:7;3686:23;3682:32;3679:52;;;3727:1;3724;3717:12;3679:52;3750:29;3769:9;3750:29;:::i;:::-;3740:39;;3798:38;3832:2;3821:9;3817:18;3798:38;:::i;:::-;3788:48;;3883:2;3872:9;3868:18;3855:32;3845:42;;3565:328;;;;;:::o;3898:348::-;3950:8;3960:6;4014:3;4007:4;3999:6;3995:17;3991:27;3981:55;;4032:1;4029;4022:12;3981:55;-1:-1:-1;4055:20:8;;-1:-1:-1;;;;;4087:30:8;;4084:50;;;4130:1;4127;4120:12;4084:50;4167:4;4159:6;4155:17;4143:29;;4219:3;4212:4;4203:6;4195;4191:19;4187:30;4184:39;4181:59;;;4236:1;4233;4226:12;4251:411;4322:6;4330;4383:2;4371:9;4362:7;4358:23;4354:32;4351:52;;;4399:1;4396;4389:12;4351:52;4439:9;4426:23;-1:-1:-1;;;;;4464:6:8;4461:30;4458:50;;;4504:1;4501;4494:12;4458:50;4543:59;4594:7;4585:6;4574:9;4570:22;4543:59;:::i;:::-;4621:8;;4517:85;;-1:-1:-1;4251:411:8;-1:-1:-1;;;;4251:411:8:o;4667:721::-;4759:6;4767;4775;4783;4836:2;4824:9;4815:7;4811:23;4807:32;4804:52;;;4852:1;4849;4842:12;4804:52;4892:9;4879:23;-1:-1:-1;;;;;4962:2:8;4954:6;4951:14;4948:34;;;4978:1;4975;4968:12;4948:34;5017:59;5068:7;5059:6;5048:9;5044:22;5017:59;:::i;:::-;5095:8;;-1:-1:-1;4991:85:8;-1:-1:-1;5183:2:8;5168:18;;5155:32;;-1:-1:-1;5199:16:8;;;5196:36;;;5228:1;5225;5218:12;5196:36;;5267:61;5320:7;5309:8;5298:9;5294:24;5267:61;:::i;:::-;4667:721;;;;-1:-1:-1;5347:8:8;-1:-1:-1;;;;4667:721:8:o;5598:160::-;5663:20;;5719:13;;5712:21;5702:32;;5692:60;;5748:1;5745;5738:12;5763:254;5828:6;5836;5889:2;5877:9;5868:7;5864:23;5860:32;5857:52;;;5905:1;5902;5895:12;5857:52;5928:29;5947:9;5928:29;:::i;:::-;5918:39;;5976:35;6007:2;5996:9;5992:18;5976:35;:::i;:::-;5966:45;;5763:254;;;;;:::o;6211:437::-;6297:6;6305;6358:2;6346:9;6337:7;6333:23;6329:32;6326:52;;;6374:1;6371;6364:12;6326:52;6414:9;6401:23;-1:-1:-1;;;;;6439:6:8;6436:30;6433:50;;;6479:1;6476;6469:12;6433:50;6518:70;6580:7;6571:6;6560:9;6556:22;6518:70;:::i;6653:180::-;6709:6;6762:2;6750:9;6741:7;6737:23;6733:32;6730:52;;;6778:1;6775;6768:12;6730:52;6801:26;6817:9;6801:26;:::i;6838:627::-;6935:6;6943;6951;6959;6967;7020:3;7008:9;6999:7;6995:23;6991:33;6988:53;;;7037:1;7034;7027:12;6988:53;7060:29;7079:9;7060:29;:::i;:::-;7050:39;;7108:38;7142:2;7131:9;7127:18;7108:38;:::i;:::-;7098:48;;7193:2;7182:9;7178:18;7165:32;7155:42;;7248:2;7237:9;7233:18;7220:32;-1:-1:-1;;;;;7267:6:8;7264:30;7261:50;;;7307:1;7304;7297:12;7261:50;7346:59;7397:7;7388:6;7377:9;7373:22;7346:59;:::i;:::-;6838:627;;;;-1:-1:-1;6838:627:8;;-1:-1:-1;7424:8:8;;7320:85;6838:627;-1:-1:-1;;;6838:627:8:o;7470:413::-;7543:6;7551;7559;7612:2;7600:9;7591:7;7587:23;7583:32;7580:52;;;7628:1;7625;7618:12;7580:52;7667:9;7654:23;7717:4;7710:5;7706:16;7699:5;7696:27;7686:55;;7737:1;7734;7727:12;7686:55;7760:5;-1:-1:-1;7784:37:8;7817:2;7802:18;;7784:37;:::i;:::-;7774:47;;7840:37;7873:2;7862:9;7858:18;7840:37;:::i;:::-;7830:47;;7470:413;;;;;:::o;7888:260::-;7956:6;7964;8017:2;8005:9;7996:7;7992:23;7988:32;7985:52;;;8033:1;8030;8023:12;7985:52;8056:29;8075:9;8056:29;:::i;:::-;8046:39;;8104:38;8138:2;8127:9;8123:18;8104:38;:::i;8153:380::-;8232:1;8228:12;;;;8275;;;8296:61;;8350:4;8342:6;8338:17;8328:27;;8296:61;8403:2;8395:6;8392:14;8372:18;8369:38;8366:161;;8449:10;8444:3;8440:20;8437:1;8430:31;8484:4;8481:1;8474:15;8512:4;8509:1;8502:15;8538:269;8605:10;8635;;;8647;;;8631:27;;8670:11;;;8667:134;;;8723:10;8718:3;8714:20;8711:1;8704:31;8758:4;8755:1;8748:15;8786:4;8783:1;8776:15;8667:134;;8538:269;;;;:::o;8812:127::-;8873:10;8868:3;8864:20;8861:1;8854:31;8904:4;8901:1;8894:15;8928:4;8925:1;8918:15;9070:518;9172:2;9167:3;9164:11;9161:421;;;9208:5;9205:1;9198:16;9252:4;9249:1;9239:18;9322:2;9310:10;9306:19;9303:1;9299:27;9293:4;9289:38;9358:4;9346:10;9343:20;9340:47;;;-1:-1:-1;9381:4:8;9340:47;9436:2;9431:3;9427:12;9424:1;9420:20;9414:4;9410:31;9400:41;;9491:81;9509:2;9502:5;9499:13;9491:81;;;9568:1;9554:16;;9535:1;9524:13;9491:81;;9764:1198;-1:-1:-1;;;;;9883:3:8;9880:27;9877:53;;;9910:18;;:::i;:::-;9939:94;10029:3;9989:38;10021:4;10015:11;9989:38;:::i;:::-;9983:4;9939:94;:::i;:::-;10059:1;10084:2;10079:3;10076:11;10101:1;10096:608;;;;10748:1;10765:3;10762:93;;;-1:-1:-1;10821:19:8;;;10808:33;10762:93;-1:-1:-1;;9721:1:8;9717:11;;;9713:24;9709:29;9699:40;9745:1;9741:11;;;9696:57;10868:78;;10069:887;;10096:608;9017:1;9010:14;;;9054:4;9041:18;;-1:-1:-1;;10132:17:8;;;10247:229;10261:7;10258:1;10255:14;10247:229;;;10350:19;;;10337:33;10322:49;;10457:4;10442:20;;;;10410:1;10398:14;;;;10277:12;10247:229;;;10251:3;10504;10495:7;10492:16;10489:159;;;10628:1;10624:6;10618:3;10612;10609:1;10605:11;10601:21;10597:34;10593:39;10580:9;10575:3;10571:19;10558:33;10554:79;10546:6;10539:95;10489:159;;;10691:1;10685:3;10682:1;10678:11;10674:19;10668:4;10661:33;10069:887;;9764:1198;;;:::o
Swarm Source
ipfs://77798a98c56e954f07284ba2e0b0f9e9ae12504a117f14e1589d59f78e075664
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.