Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 3,594 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Withdraw Asterix... | 21274887 | 2 hrs ago | IN | 0 ETH | 0.00141458 | ||||
Deposit Asterix ... | 21270954 | 15 hrs ago | IN | 0 ETH | 0.00156309 | ||||
Withdraw Asterix... | 21270941 | 15 hrs ago | IN | 0 ETH | 0.00161619 | ||||
Safe Transfer Fr... | 21270926 | 15 hrs ago | IN | 0 ETH | 0.00059824 | ||||
Set Single Trans... | 21270920 | 15 hrs ago | IN | 0 ETH | 0.00035713 | ||||
Deposit Asterix ... | 21270374 | 17 hrs ago | IN | 0 ETH | 0.00070484 | ||||
Deposit Asterix ... | 21269455 | 20 hrs ago | IN | 0 ETH | 0.00073529 | ||||
Deposit Asterix ... | 21269282 | 21 hrs ago | IN | 0 ETH | 0.00049475 | ||||
Deposit Asterix ... | 21269070 | 22 hrs ago | IN | 0 ETH | 0.00071594 | ||||
Deposit Asterix ... | 21265202 | 35 hrs ago | IN | 0 ETH | 0.00125796 | ||||
Deposit Asterix ... | 21264159 | 38 hrs ago | IN | 0 ETH | 0.00428104 | ||||
Deposit Asterix ... | 21263704 | 40 hrs ago | IN | 0 ETH | 0.00198919 | ||||
Deposit Asterix ... | 21255263 | 2 days ago | IN | 0 ETH | 0.00104171 | ||||
Deposit Asterix ... | 21253012 | 3 days ago | IN | 0 ETH | 0.00055448 | ||||
Deposit Asterix ... | 21252999 | 3 days ago | IN | 0 ETH | 0.00197731 | ||||
Deposit Asterix ... | 21251797 | 3 days ago | IN | 0 ETH | 0.00316761 | ||||
Withdraw Asterix... | 21250712 | 3 days ago | IN | 0 ETH | 0.00221491 | ||||
Withdraw Asterix... | 21243960 | 4 days ago | IN | 0 ETH | 0.00148103 | ||||
Deposit Asterix ... | 21239151 | 5 days ago | IN | 0 ETH | 0.00159629 | ||||
Deposit Asterix ... | 21235148 | 5 days ago | IN | 0 ETH | 0.00443947 | ||||
Deposit Asterix ... | 21235070 | 5 days ago | IN | 0 ETH | 0.00143016 | ||||
Withdraw Asterix... | 21235045 | 5 days ago | IN | 0 ETH | 0.0048344 | ||||
Deposit Asterix ... | 21234300 | 5 days ago | IN | 0 ETH | 0.00215842 | ||||
Deposit Asterix ... | 21234027 | 5 days ago | IN | 0 ETH | 0.00121585 | ||||
Deposit Asterix ... | 21233860 | 5 days ago | IN | 0 ETH | 0.00347925 |
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
19637001 | 228 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
StakingVaultManager
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 Staking vault manager class. contract StakingVaultManager is VaultManager { /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* CUSTOM ERRORS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev The value is locked. error Locked(); /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* STORAGE */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @dev Address of the vault's proxy. address public vaultProxy; /// @dev Whether the vault's proxy is locked. bool public vaultProxyLocked; /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* CONSTRUCTOR */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ constructor() { _initializeVaultManager( msg.sender == 0x0000000000FFe8B47B3e2130213B802212439497 ? tx.origin : msg.sender ); } /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/ /* VAULT OPERATIONS */ /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/ /// @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 onlyOwner { vaultProxyLocked = true; } /// @dev Sets the vault proxy. function setVaultProxy(address proxy) public virtual onlyOwner { if (vaultProxyLocked) revert Locked(); vaultProxy = proxy; } }
// 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) let retSize := add(mload(a), 0x40) // Right pad with zeroes. Just in case the string is produced // by a method that doesn't zero right pad. mstore(add(retStart, retSize), 0) // Store the return offset. mstore(retStart, 0x20) // End the transaction, returning the string. return(retStart, retSize) } } }
// 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":"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":"Reentrancy","type":"error"},{"inputs":[],"name":"SingleTransferNotEnabled","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"},{"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":"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":"account","type":"address"},{"internalType":"uint256","name":"id","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":[{"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":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"result","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":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockVaultProxy","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":"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":"operator","type":"address"},{"internalType":"bool","name":"isApproved","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":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"}],"name":"setNameAndSymbol","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","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":"uint256","name":"tokenId","type":"uint256"}],"name":"singleTransferEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","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":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","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":[{"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"}]
Contract Creation Code
608060405234801561000f575f80fd5b506100356effe8b47b3e2130213b802212439497331461002f573361003a565b3261003a565b610113565b61004381610054565b6100518163800000006100ac565b50565b638b78c6d81980541561006e57630dc149f05f526004601cfd5b6001600160a01b03909116801560ff1b8117909155805f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b6100b8828260016100bc565b5050565b638b78c6d8600c52825f526020600c208054838117836100dd575080841681185b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f80a3505050505050565b612204806101205f395ff3fe608060405260043610610341575f3560e01c80636352211e116101bd578063aeb84f24116100f2578063cf70698a11610092578063f2fde38b1161006d578063f2fde38b1461098a578063fbcd21591461099d578063fee81cf4146109bc578063ff87a5f5146109ed575f80fd5b8063cf70698a14610924578063e985e9c514610943578063f04e283e14610977575f80fd5b8063b69e0643116100cd578063b69e0643146108bf578063b88d4fde146108d3578063c5975788146108e6578063c87b56dd14610905575f80fd5b8063aeb84f2414610862578063b19c3ca314610881578063b50fd681146108a0575f80fd5b8063905e34411161015d578063983525bf11610138578063983525bf146107c7578063a22cb465146107e6578063a6caeb4b14610805578063a7e76d4814610842575f80fd5b8063905e34411461076f57806394a7c3ef1461079157806395d89b41146107b3575f80fd5b8063893a537211610198578063893a5372146106db5780638c13a453146107015780638d654023146107205780638da5cb5b14610757575f80fd5b80636352211e1461069557806370a08231146106b4578063715018a6146106d3575f80fd5b806329a4927a116102935780634a4ee7b11161023357806354d1f13d1161020e57806354d1f13d1461063b57806355f804b3146106435780635a446215146106625780635d12928b14610681575f80fd5b80634a4ee7b1146105d457806350a534c7146105e7578063514e62fc14610606575f80fd5b806336fe3cb81161026e57806336fe3cb814610564578063397bfe5514610583578063398e6cb7146105a257806342842e0e146105c1575f80fd5b806329a4927a146104fd5780632de948071461051c578063302ad53a1461054d575f80fd5b806318160ddd116102fe5780631c10893f116102d95780631c10893f1461049a5780631cd64df4146104ad57806323b872dd146104e257806325692962146104f5575f80fd5b806318160ddd14610441578063183a4f6e146104685780631a966d701461047b575f80fd5b806301ffc9a71461034557806306fdde03146103965780630709df45146103b7578063081812fc146103ee578063095ea7b31461040d57806310a96a0114610422575b5f80fd5b348015610350575f80fd5b5061038161035f366004611cf6565b6301ffc9a760e09190911c9081146380ac58cd821417635b5e139f9091141790565b60405190151581526020015b60405180910390f35b3480156103a1575f80fd5b506103aa610a0c565b60405161038d9190611d1d565b3480156103c2575f80fd5b506103d66103d1366004611d68565b610a9c565b6040516001600160a01b03909116815260200161038d565b3480156103f9575f80fd5b506103d6610408366004611d81565b610aac565b61042061041b366004611d98565b610ae7565b005b34801561042d575f80fd5b506103d661043c366004611e07565b610af6565b34801561044c575f80fd5b506004546001600160401b03165b60405190815260200161038d565b610420610476366004611d81565b610b9b565b348015610486575f80fd5b506103d6610495366004611d68565b610ba8565b6104206104a8366004611d98565b610bee565b3480156104b8575f80fd5b506103816104c7366004611d98565b638b78c6d8600c9081525f9290925260209091205481161490565b6104206104f0366004611e55565b610c00565b610420610d05565b348015610508575f80fd5b506103d6610517366004611e07565b610d51565b348015610527575f80fd5b5061045a610536366004611d68565b638b78c6d8600c9081525f91909152602090205490565b348015610558575f80fd5b5061045a638000000081565b34801561056f575f80fd5b5061045a61057e366004611d68565b610dc0565b34801561058e575f80fd5b5061042061059d366004611d68565b610e05565b3480156105ad575f80fd5b506103d66105bc366004611d81565b610e69565b6104206105cf366004611e55565b610e74565b6104206105e2366004611d98565b610ea0565b3480156105f2575f80fd5b5061045a610601366004611d68565b610eb2565b348015610611575f80fd5b50610381610620366004611d98565b638b78c6d8600c9081525f9290925260209091205416151590565b610420610f19565b34801561064e575f80fd5b5061042061065d366004611ecb565b610f52565b34801561066d575f80fd5b5061042061067c366004611f09565b610f73565b34801561068c575f80fd5b506103d6610fa4565b3480156106a0575f80fd5b506103d66106af366004611d81565b610fb3565b3480156106bf575f80fd5b5061045a6106ce366004611d68565b610fd6565b61042061100e565b3480156106e6575f80fd5b506004546103d690600160401b90046001600160a01b031681565b34801561070c575f80fd5b506103d661071b366004611d81565b611021565b34801561072b575f80fd5b5060045461073f906001600160401b031681565b6040516001600160401b03909116815260200161038d565b348015610762575f80fd5b50638b78c6d819546103d6565b34801561077a575f80fd5b506103d66eca73a6df4c58b84c5b4b847fe8ff3981565b34801561079c575f80fd5b506103d66ec26fabfe894d13233d5ec73f61cc7281565b3480156107be575f80fd5b506103aa611033565b3480156107d2575f80fd5b506103d66107e1366004611d98565b611042565b3480156107f1575f80fd5b50610420610800366004611f7e565b6110cb565b348015610810575f80fd5b5061038161081f366004611d81565b5f818152673ec412a9852d173d60c11b601c52602090208101015460a01c151590565b34801561084d575f80fd5b5060045461038190600160e01b900460ff1681565b34801561086d575f80fd5b506103d661087c366004611faf565b61111e565b34801561088c575f80fd5b5061042061089b366004611fe1565b611131565b3480156108ab575f80fd5b506103d66108ba366004611d81565b6111f2565b3480156108ca575f80fd5b506104206111fd565b6104206108e1366004611ffa565b61121a565b3480156108f1575f80fd5b506103d6610900366004611d68565b611274565b348015610910575f80fd5b506103aa61091f366004611d81565b6112b5565b34801561092f575f80fd5b506103d661093e366004611faf565b611433565b34801561094e575f80fd5b5061038161095d366004612063565b601c52670a5a2e7a000000006008525f526030600c205490565b610420610985366004611d68565b61143f565b610420610998366004611d68565b611479565b3480156109a8575f80fd5b506103d66109b7366004611d98565b61149f565b3480156109c7575f80fd5b5061045a6109d6366004611d68565b63389a75e1600c9081525f91909152602090205490565b3480156109f8575f80fd5b50610420610a07366004611d68565b6114f4565b606060018054610a1b9061208b565b80601f0160208091040260200160405190810160405280929190818152602001828054610a479061208b565b8015610a925780601f10610a6957610100808354040283529160200191610a92565b820191905f5260205f20905b815481529060010190602001808311610a7557829003601f168201915b5050505050905090565b5f610aa682611524565b92915050565b5f815f52673ec412a9852d173d60c11b601c5260205f2082018201805460601b610add5763ceea21b65f526004601cfd5b6001015492915050565b610af2338383611544565b5050565b5f68929eee149b4bd212685c15610b145763ab143c065f526004601cfd5b3068929eee149b4bd212685d610b2933610a9c565b90506001600160a01b038116610b525760405163425ef92760e01b815260040160405180910390fd5b5f5b82811015610b8657610b7e826ec26fabfe894d13233d5ec73f61cc7287600585901b8801356115de565b600101610b54565b505b5f68929eee149b4bd212685d9392505050565b610ba53382611602565b50565b5f68929eee149b4bd212685c15610bc65763ab143c065f526004601cfd5b3068929eee149b4bd212685d610bdb8261160d565b90505f68929eee149b4bd212685d919050565b610bf66116f1565b610af2828261170b565b610c0b838383611717565b5f818152673ec412a9852d173d60c11b3317601c52602090208101810180546001600160a01b039485169493841693811691908286148302610c5c5767ceea21b6a1148100831560021b526004601cfd5b855f528160010154925082331486331417610c88576030600c2054610c8857634b6e7f185f526004601cfd5b8215610c95575f82600101555b85851818905550601c600c81812080545f190190555f84905220805460010163ffffffff81168402610cd65767ea553b3401336cea841560021b526004601cfd5b90558082847fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f38a45b505050565b5f6202a3006001600160401b03164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a250565b5f68929eee149b4bd212685c15610d6f5763ab143c065f526004601cfd5b3068929eee149b4bd212685d610d848461160d565b90505f5b82811015610dba57610db2826ec26fabfe894d13233d5ec73f61cc7233600585901b8801356117ba565b600101610d88565b50610b88565b5f80610dcb836117fb565b90505f610dd78261180a565b9050836001600160a01b0316610dec82610a9c565b6001600160a01b031603610dfe578192505b5050919050565b610e0d6116f1565b600454600160e01b900460ff1615610e38576040516303cb96db60e21b815260040160405180910390fd5b600480546001600160a01b03909216600160401b0268010000000000000000600160e01b0319909216919091179055565b5f610aa63383611042565b610e7f838383610c00565b813b15610d0057610d0083838360405180602001604052805f815250611831565b610ea86116f1565b610af28282611602565b5f80610ebd83610a9c565b90506001600160a01b038116610ed557505f92915050565b670de0b6b3a7640000610ef76ec26fabfe894d13233d5ec73f61cc72836118b2565b02610f116eca73a6df4c58b84c5b4b847fe8ff39836118b2565b039392505050565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2565b6380000000610f60816118dc565b6003610f6d838583612115565b50505050565b6380000000610f81816118dc565b6001610f8e858783612115565b506002610f9c838583612115565b505050505050565b5f610fae33610ba8565b905090565b5f610fbd8261180a565b905080610fd15763ceea21b65f526004601cfd5b919050565b5f81610fe957638f4eb6045f526004601cfd5b673ec412a9852d173d60c11b601c52815f5263ffffffff601c600c2054169050919050565b6110166116f1565b61101f5f61190d565b565b5f610aa661102e8361180a565b611524565b606060028054610a1b9061208b565b5f68929eee149b4bd212685c156110605763ab143c065f526004601cfd5b3068929eee149b4bd212685d61107533610a9c565b90506001600160a01b03811661109e5760405163425ef92760e01b815260040160405180910390fd5b6110b9816eca73a6df4c58b84c5b4b847fe8ff398585611953565b5f68929eee149b4bd212685d92915050565b801515905081601c52670a5a2e7a00000000600852335f52806030600c2055805f528160601b60601c337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160205fa35050565b5f61112a338484610af6565b9392505050565b68929eee149b4bd212685c1561114e5763ab143c065f526004601cfd5b3068929eee149b4bd212685d5f61116433610a9c565b90506001600160a01b03811661118d5760405163425ef92760e01b815260040160405180910390fd5b5f611197826117fb565b90506111a581841515611977565b807ffaa8d8eedc81dcb9ee1d63753820c60f27794f91eb8d0725891f0e0b4e4730c1846040516111d9911515815260200190565b60405180910390a250505f68929eee149b4bd212685d50565b5f610aa6338361149f565b6112056116f1565b6004805460ff60e01b1916600160e01b179055565b611225858585610c00565b833b1561126d5761126d85858585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061183192505050565b5050505050565b5f8061127f836117fb565b90505f61128b8261180a565b9050836001600160a01b03166112a082610a9c565b6001600160a01b031603610dfe579392505050565b60606112de825f818152673ec412a9852d173d60c11b601c52602090208101015460601b151590565b6112fb5760405163677510db60e11b815260040160405180910390fd5b5f546001600160a01b0316801561135f57604051915063c87b56dd5f52826020525f806024601c845afa611331573d5f833e3d82fd5b60205f803e60205f51833e815160205f5101602084013e815160208301015f8152602081016040525061142d565b6003805461136c9061208b565b15905061142d5761142a600380546113839061208b565b80601f01602080910402602001604051908101604052809291908181526020018280546113af9061208b565b80156113fa5780601f106113d1576101008083540402835291602001916113fa565b820191905f5260205f20905b8154815290600101906020018083116113dd57829003601f168201915b5050505050604051806040016040528060048152602001637b69647d60e01b815250611425866119a6565b6119e8565b91505b50919050565b5f61112a338484610d51565b6114476116f1565b63389a75e1600c52805f526020600c20805442111561146d57636f5e88185f526004601cfd5b5f9055610ba58161190d565b6114816116f1565b8060601b61149657637448fbae5f526004601cfd5b610ba58161190d565b5f68929eee149b4bd212685c156114bd5763ab143c065f526004601cfd5b3068929eee149b4bd212685d6114d28361160d565b90506114ef816eca73a6df4c58b84c5b4b847fe8ff3933856117ba565b6110b9565b6380000000611502816118dc565b505f80546001600160a01b0319166001600160a01b0392909216919091179055565b673ec412a9852d173d60c11b601c9081525f91909152600c205460201c90565b5f1960601c82811692508381169350815f5283673ec412a9852d173d60c11b17601c5260205f2082018201805482169150816115875763ceea21b65f526004601cfd5b8185148515176115ab57815f526030600c20546115ab57634b6e7f185f526004601cfd5b6001018390558183827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f38a450505050565b610f6d84846323b872dd876001600160a01b0316866001600160a01b031686611b08565b610af282825f611b6f565b5f61161782611524565b90506001600160a01b0381161561162d57919050565b600454600160401b90046001600160a01b03168061165e57604051630873153160e11b815260040160405180910390fd5b600480546001600160401b038082166001011667ffffffffffffffff19909116811790915561168e84825f611bc6565b61169b825f463085611c58565b9250826001600160a01b031681856001600160a01b03167f019b61d9140f8784f4eed1e66569335630452af2d974ceb13d3e706c0786729660405160405180910390a4610dfe84846001600160a01b0316611cca565b638b78c6d81954331461101f576382b429005f526004601cfd5b610af282826001611b6f565b61172082611524565b6001600160e01b0316156117475760405163759be05b60e11b815260040160405180910390fd5b6001600160a01b03831615610d00575f818152673ec412a9852d173d60c11b601c5260208120820182015460a01c90036117945760405163768a0e2b60e01b815260040160405180910390fd5b61179e815f611977565b6117b0826117ab85611524565b611cca565b610d00835f611cca565b60405181606052846040528260601b602c526323b872dd60601b600c5260205f6064601c5f885af16117ee573d5f823e3d81fd5b5f60605260405250505050565b5f6020608d5f843c50505f5190565b5f818152673ec412a9852d173d60c11b601c5260209020810101546001600160a01b031690565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a08401528015611878578060c08401826020870160045afa505b60208360a48301601c86015f8a5af1611899573d15611899573d5f843e3d83fd5b508060e01b825114610f9c5763d1a57ed65f526004601cfd5b5f816014526370a0823160601b5f5260208060246010865afa601f3d111660205102905092915050565b638b78c6d819543314610ba557638b78c6d8600c52335f52806020600c205416610ba5576382b429005f526004601cfd5b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3811560ff1b8217905550565b610f6d848463a9059cbb856001600160a01b0316856001600160a01b03165f611b08565b815f52673ec412a9852d173d60c11b601c5260205f208201820180548060a01c831860a01b8118825550505050565b60606080604051019050602081016040525f8152805f19835b928101926030600a8206018453600a9004806119bf575050819003601f19909101908152919050565b60608351835183516020870196506020860195506020850194506020604051019350828701838311611ab8576001838203015f60208510611a2857508388205b601f851660200360031b89515b8b51818118831c611a9f578315611a6f5783888e2014611a6f57808a5260019c8d019c90990198848d10611a695750611ab3565b50611a35565b5f5b8b8101518b820152602001878110611a7157509b87019b988601988715611a9f57848d10611a695750611ab3565b895260019b8c019b90980197838c10611a35575b505050505b84935060206040510194508781038585030192505b80881015611ae8578751845260209788019790930192611acd565b50505f818401908152602001604052601f19909201918252509392505050565b604051856014820152635194544760601b81525f6034820152608060548201525f60748201528460988201528360b88201528260d88201528160f8820152606460948201525f38610124601084015f8b5af1611b66573d5f823e3d81fd5b50505050505050565b638b78c6d8600c52825f526020600c20805483811783611b90575080841681185b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f80a3505050505050565b611bd15f8484611717565b8260601b60601c9250815f52673ec412a9852d173d60c11b601c52828160a01b1760205f208301830155825f52601c600c20600181540163ffffffff81168502611c2a5767ea553b3401336cea851560021b526004601cfd5b905581835f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8138a4505050565b5f6040518660148201528560348201528460548201528360601b60601c6074820152826094820152638a54c52f60601b815260205f60a4601084015f6f6551c19487814612e58fe068137757585af1601f3d1116611cbd576320188a595f526004601cfd5b50505f5195945050505050565b673ec412a9852d173d60c11b601c52815f52601c600c2080548060201c831860201b8118825550505050565b5f60208284031215611d06575f80fd5b81356001600160e01b03198116811461112a575f80fd5b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b80356001600160a01b0381168114610fd1575f80fd5b5f60208284031215611d78575f80fd5b61112a82611d52565b5f60208284031215611d91575f80fd5b5035919050565b5f8060408385031215611da9575f80fd5b611db283611d52565b946020939093013593505050565b5f8083601f840112611dd0575f80fd5b5081356001600160401b03811115611de6575f80fd5b6020830191508360208260051b8501011115611e00575f80fd5b9250929050565b5f805f60408486031215611e19575f80fd5b611e2284611d52565b925060208401356001600160401b03811115611e3c575f80fd5b611e4886828701611dc0565b9497909650939450505050565b5f805f60608486031215611e67575f80fd5b611e7084611d52565b9250611e7e60208501611d52565b9150604084013590509250925092565b5f8083601f840112611e9e575f80fd5b5081356001600160401b03811115611eb4575f80fd5b602083019150836020828501011115611e00575f80fd5b5f8060208385031215611edc575f80fd5b82356001600160401b03811115611ef1575f80fd5b611efd85828601611e8e565b90969095509350505050565b5f805f8060408587031215611f1c575f80fd5b84356001600160401b0380821115611f32575f80fd5b611f3e88838901611e8e565b90965094506020870135915080821115611f56575f80fd5b50611f6387828801611e8e565b95989497509550505050565b80358015158114610fd1575f80fd5b5f8060408385031215611f8f575f80fd5b611f9883611d52565b9150611fa660208401611f6f565b90509250929050565b5f8060208385031215611fc0575f80fd5b82356001600160401b03811115611fd5575f80fd5b611efd85828601611dc0565b5f60208284031215611ff1575f80fd5b61112a82611f6f565b5f805f805f6080868803121561200e575f80fd5b61201786611d52565b945061202560208701611d52565b93506040860135925060608601356001600160401b03811115612046575f80fd5b61205288828901611e8e565b969995985093965092949392505050565b5f8060408385031215612074575f80fd5b61207d83611d52565b9150611fa660208401611d52565b600181811c9082168061209f57607f821691505b60208210810361142d57634e487b7160e01b5f52602260045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b601f821115610d0057805f5260205f20601f840160051c810160208510156120f65750805b601f840160051c820191505b8181101561126d575f8155600101612102565b6001600160401b0383111561212c5761212c6120bd565b6121408361213a835461208b565b836120d1565b5f601f841160018114612171575f851561215a5750838201355b5f19600387901b1c1916600186901b17835561126d565b5f83815260208120601f198716915b828110156121a05786850135825560209485019460019092019101612180565b50868210156121bc575f1960f88860031b161c19848701351681555b505060018560011b018355505050505056fea26469706673582212207b184e74ca114f5efbf54d0fbe4087ef6dba4ce52b7914fe01c91add502a40dc64736f6c63430008190033
Deployed Bytecode
0x608060405260043610610341575f3560e01c80636352211e116101bd578063aeb84f24116100f2578063cf70698a11610092578063f2fde38b1161006d578063f2fde38b1461098a578063fbcd21591461099d578063fee81cf4146109bc578063ff87a5f5146109ed575f80fd5b8063cf70698a14610924578063e985e9c514610943578063f04e283e14610977575f80fd5b8063b69e0643116100cd578063b69e0643146108bf578063b88d4fde146108d3578063c5975788146108e6578063c87b56dd14610905575f80fd5b8063aeb84f2414610862578063b19c3ca314610881578063b50fd681146108a0575f80fd5b8063905e34411161015d578063983525bf11610138578063983525bf146107c7578063a22cb465146107e6578063a6caeb4b14610805578063a7e76d4814610842575f80fd5b8063905e34411461076f57806394a7c3ef1461079157806395d89b41146107b3575f80fd5b8063893a537211610198578063893a5372146106db5780638c13a453146107015780638d654023146107205780638da5cb5b14610757575f80fd5b80636352211e1461069557806370a08231146106b4578063715018a6146106d3575f80fd5b806329a4927a116102935780634a4ee7b11161023357806354d1f13d1161020e57806354d1f13d1461063b57806355f804b3146106435780635a446215146106625780635d12928b14610681575f80fd5b80634a4ee7b1146105d457806350a534c7146105e7578063514e62fc14610606575f80fd5b806336fe3cb81161026e57806336fe3cb814610564578063397bfe5514610583578063398e6cb7146105a257806342842e0e146105c1575f80fd5b806329a4927a146104fd5780632de948071461051c578063302ad53a1461054d575f80fd5b806318160ddd116102fe5780631c10893f116102d95780631c10893f1461049a5780631cd64df4146104ad57806323b872dd146104e257806325692962146104f5575f80fd5b806318160ddd14610441578063183a4f6e146104685780631a966d701461047b575f80fd5b806301ffc9a71461034557806306fdde03146103965780630709df45146103b7578063081812fc146103ee578063095ea7b31461040d57806310a96a0114610422575b5f80fd5b348015610350575f80fd5b5061038161035f366004611cf6565b6301ffc9a760e09190911c9081146380ac58cd821417635b5e139f9091141790565b60405190151581526020015b60405180910390f35b3480156103a1575f80fd5b506103aa610a0c565b60405161038d9190611d1d565b3480156103c2575f80fd5b506103d66103d1366004611d68565b610a9c565b6040516001600160a01b03909116815260200161038d565b3480156103f9575f80fd5b506103d6610408366004611d81565b610aac565b61042061041b366004611d98565b610ae7565b005b34801561042d575f80fd5b506103d661043c366004611e07565b610af6565b34801561044c575f80fd5b506004546001600160401b03165b60405190815260200161038d565b610420610476366004611d81565b610b9b565b348015610486575f80fd5b506103d6610495366004611d68565b610ba8565b6104206104a8366004611d98565b610bee565b3480156104b8575f80fd5b506103816104c7366004611d98565b638b78c6d8600c9081525f9290925260209091205481161490565b6104206104f0366004611e55565b610c00565b610420610d05565b348015610508575f80fd5b506103d6610517366004611e07565b610d51565b348015610527575f80fd5b5061045a610536366004611d68565b638b78c6d8600c9081525f91909152602090205490565b348015610558575f80fd5b5061045a638000000081565b34801561056f575f80fd5b5061045a61057e366004611d68565b610dc0565b34801561058e575f80fd5b5061042061059d366004611d68565b610e05565b3480156105ad575f80fd5b506103d66105bc366004611d81565b610e69565b6104206105cf366004611e55565b610e74565b6104206105e2366004611d98565b610ea0565b3480156105f2575f80fd5b5061045a610601366004611d68565b610eb2565b348015610611575f80fd5b50610381610620366004611d98565b638b78c6d8600c9081525f9290925260209091205416151590565b610420610f19565b34801561064e575f80fd5b5061042061065d366004611ecb565b610f52565b34801561066d575f80fd5b5061042061067c366004611f09565b610f73565b34801561068c575f80fd5b506103d6610fa4565b3480156106a0575f80fd5b506103d66106af366004611d81565b610fb3565b3480156106bf575f80fd5b5061045a6106ce366004611d68565b610fd6565b61042061100e565b3480156106e6575f80fd5b506004546103d690600160401b90046001600160a01b031681565b34801561070c575f80fd5b506103d661071b366004611d81565b611021565b34801561072b575f80fd5b5060045461073f906001600160401b031681565b6040516001600160401b03909116815260200161038d565b348015610762575f80fd5b50638b78c6d819546103d6565b34801561077a575f80fd5b506103d66eca73a6df4c58b84c5b4b847fe8ff3981565b34801561079c575f80fd5b506103d66ec26fabfe894d13233d5ec73f61cc7281565b3480156107be575f80fd5b506103aa611033565b3480156107d2575f80fd5b506103d66107e1366004611d98565b611042565b3480156107f1575f80fd5b50610420610800366004611f7e565b6110cb565b348015610810575f80fd5b5061038161081f366004611d81565b5f818152673ec412a9852d173d60c11b601c52602090208101015460a01c151590565b34801561084d575f80fd5b5060045461038190600160e01b900460ff1681565b34801561086d575f80fd5b506103d661087c366004611faf565b61111e565b34801561088c575f80fd5b5061042061089b366004611fe1565b611131565b3480156108ab575f80fd5b506103d66108ba366004611d81565b6111f2565b3480156108ca575f80fd5b506104206111fd565b6104206108e1366004611ffa565b61121a565b3480156108f1575f80fd5b506103d6610900366004611d68565b611274565b348015610910575f80fd5b506103aa61091f366004611d81565b6112b5565b34801561092f575f80fd5b506103d661093e366004611faf565b611433565b34801561094e575f80fd5b5061038161095d366004612063565b601c52670a5a2e7a000000006008525f526030600c205490565b610420610985366004611d68565b61143f565b610420610998366004611d68565b611479565b3480156109a8575f80fd5b506103d66109b7366004611d98565b61149f565b3480156109c7575f80fd5b5061045a6109d6366004611d68565b63389a75e1600c9081525f91909152602090205490565b3480156109f8575f80fd5b50610420610a07366004611d68565b6114f4565b606060018054610a1b9061208b565b80601f0160208091040260200160405190810160405280929190818152602001828054610a479061208b565b8015610a925780601f10610a6957610100808354040283529160200191610a92565b820191905f5260205f20905b815481529060010190602001808311610a7557829003601f168201915b5050505050905090565b5f610aa682611524565b92915050565b5f815f52673ec412a9852d173d60c11b601c5260205f2082018201805460601b610add5763ceea21b65f526004601cfd5b6001015492915050565b610af2338383611544565b5050565b5f68929eee149b4bd212685c15610b145763ab143c065f526004601cfd5b3068929eee149b4bd212685d610b2933610a9c565b90506001600160a01b038116610b525760405163425ef92760e01b815260040160405180910390fd5b5f5b82811015610b8657610b7e826ec26fabfe894d13233d5ec73f61cc7287600585901b8801356115de565b600101610b54565b505b5f68929eee149b4bd212685d9392505050565b610ba53382611602565b50565b5f68929eee149b4bd212685c15610bc65763ab143c065f526004601cfd5b3068929eee149b4bd212685d610bdb8261160d565b90505f68929eee149b4bd212685d919050565b610bf66116f1565b610af2828261170b565b610c0b838383611717565b5f818152673ec412a9852d173d60c11b3317601c52602090208101810180546001600160a01b039485169493841693811691908286148302610c5c5767ceea21b6a1148100831560021b526004601cfd5b855f528160010154925082331486331417610c88576030600c2054610c8857634b6e7f185f526004601cfd5b8215610c95575f82600101555b85851818905550601c600c81812080545f190190555f84905220805460010163ffffffff81168402610cd65767ea553b3401336cea841560021b526004601cfd5b90558082847fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f38a45b505050565b5f6202a3006001600160401b03164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a250565b5f68929eee149b4bd212685c15610d6f5763ab143c065f526004601cfd5b3068929eee149b4bd212685d610d848461160d565b90505f5b82811015610dba57610db2826ec26fabfe894d13233d5ec73f61cc7233600585901b8801356117ba565b600101610d88565b50610b88565b5f80610dcb836117fb565b90505f610dd78261180a565b9050836001600160a01b0316610dec82610a9c565b6001600160a01b031603610dfe578192505b5050919050565b610e0d6116f1565b600454600160e01b900460ff1615610e38576040516303cb96db60e21b815260040160405180910390fd5b600480546001600160a01b03909216600160401b0268010000000000000000600160e01b0319909216919091179055565b5f610aa63383611042565b610e7f838383610c00565b813b15610d0057610d0083838360405180602001604052805f815250611831565b610ea86116f1565b610af28282611602565b5f80610ebd83610a9c565b90506001600160a01b038116610ed557505f92915050565b670de0b6b3a7640000610ef76ec26fabfe894d13233d5ec73f61cc72836118b2565b02610f116eca73a6df4c58b84c5b4b847fe8ff39836118b2565b039392505050565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2565b6380000000610f60816118dc565b6003610f6d838583612115565b50505050565b6380000000610f81816118dc565b6001610f8e858783612115565b506002610f9c838583612115565b505050505050565b5f610fae33610ba8565b905090565b5f610fbd8261180a565b905080610fd15763ceea21b65f526004601cfd5b919050565b5f81610fe957638f4eb6045f526004601cfd5b673ec412a9852d173d60c11b601c52815f5263ffffffff601c600c2054169050919050565b6110166116f1565b61101f5f61190d565b565b5f610aa661102e8361180a565b611524565b606060028054610a1b9061208b565b5f68929eee149b4bd212685c156110605763ab143c065f526004601cfd5b3068929eee149b4bd212685d61107533610a9c565b90506001600160a01b03811661109e5760405163425ef92760e01b815260040160405180910390fd5b6110b9816eca73a6df4c58b84c5b4b847fe8ff398585611953565b5f68929eee149b4bd212685d92915050565b801515905081601c52670a5a2e7a00000000600852335f52806030600c2055805f528160601b60601c337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160205fa35050565b5f61112a338484610af6565b9392505050565b68929eee149b4bd212685c1561114e5763ab143c065f526004601cfd5b3068929eee149b4bd212685d5f61116433610a9c565b90506001600160a01b03811661118d5760405163425ef92760e01b815260040160405180910390fd5b5f611197826117fb565b90506111a581841515611977565b807ffaa8d8eedc81dcb9ee1d63753820c60f27794f91eb8d0725891f0e0b4e4730c1846040516111d9911515815260200190565b60405180910390a250505f68929eee149b4bd212685d50565b5f610aa6338361149f565b6112056116f1565b6004805460ff60e01b1916600160e01b179055565b611225858585610c00565b833b1561126d5761126d85858585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061183192505050565b5050505050565b5f8061127f836117fb565b90505f61128b8261180a565b9050836001600160a01b03166112a082610a9c565b6001600160a01b031603610dfe579392505050565b60606112de825f818152673ec412a9852d173d60c11b601c52602090208101015460601b151590565b6112fb5760405163677510db60e11b815260040160405180910390fd5b5f546001600160a01b0316801561135f57604051915063c87b56dd5f52826020525f806024601c845afa611331573d5f833e3d82fd5b60205f803e60205f51833e815160205f5101602084013e815160208301015f8152602081016040525061142d565b6003805461136c9061208b565b15905061142d5761142a600380546113839061208b565b80601f01602080910402602001604051908101604052809291908181526020018280546113af9061208b565b80156113fa5780601f106113d1576101008083540402835291602001916113fa565b820191905f5260205f20905b8154815290600101906020018083116113dd57829003601f168201915b5050505050604051806040016040528060048152602001637b69647d60e01b815250611425866119a6565b6119e8565b91505b50919050565b5f61112a338484610d51565b6114476116f1565b63389a75e1600c52805f526020600c20805442111561146d57636f5e88185f526004601cfd5b5f9055610ba58161190d565b6114816116f1565b8060601b61149657637448fbae5f526004601cfd5b610ba58161190d565b5f68929eee149b4bd212685c156114bd5763ab143c065f526004601cfd5b3068929eee149b4bd212685d6114d28361160d565b90506114ef816eca73a6df4c58b84c5b4b847fe8ff3933856117ba565b6110b9565b6380000000611502816118dc565b505f80546001600160a01b0319166001600160a01b0392909216919091179055565b673ec412a9852d173d60c11b601c9081525f91909152600c205460201c90565b5f1960601c82811692508381169350815f5283673ec412a9852d173d60c11b17601c5260205f2082018201805482169150816115875763ceea21b65f526004601cfd5b8185148515176115ab57815f526030600c20546115ab57634b6e7f185f526004601cfd5b6001018390558183827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f38a450505050565b610f6d84846323b872dd876001600160a01b0316866001600160a01b031686611b08565b610af282825f611b6f565b5f61161782611524565b90506001600160a01b0381161561162d57919050565b600454600160401b90046001600160a01b03168061165e57604051630873153160e11b815260040160405180910390fd5b600480546001600160401b038082166001011667ffffffffffffffff19909116811790915561168e84825f611bc6565b61169b825f463085611c58565b9250826001600160a01b031681856001600160a01b03167f019b61d9140f8784f4eed1e66569335630452af2d974ceb13d3e706c0786729660405160405180910390a4610dfe84846001600160a01b0316611cca565b638b78c6d81954331461101f576382b429005f526004601cfd5b610af282826001611b6f565b61172082611524565b6001600160e01b0316156117475760405163759be05b60e11b815260040160405180910390fd5b6001600160a01b03831615610d00575f818152673ec412a9852d173d60c11b601c5260208120820182015460a01c90036117945760405163768a0e2b60e01b815260040160405180910390fd5b61179e815f611977565b6117b0826117ab85611524565b611cca565b610d00835f611cca565b60405181606052846040528260601b602c526323b872dd60601b600c5260205f6064601c5f885af16117ee573d5f823e3d81fd5b5f60605260405250505050565b5f6020608d5f843c50505f5190565b5f818152673ec412a9852d173d60c11b601c5260209020810101546001600160a01b031690565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a08401528015611878578060c08401826020870160045afa505b60208360a48301601c86015f8a5af1611899573d15611899573d5f843e3d83fd5b508060e01b825114610f9c5763d1a57ed65f526004601cfd5b5f816014526370a0823160601b5f5260208060246010865afa601f3d111660205102905092915050565b638b78c6d819543314610ba557638b78c6d8600c52335f52806020600c205416610ba5576382b429005f526004601cfd5b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3811560ff1b8217905550565b610f6d848463a9059cbb856001600160a01b0316856001600160a01b03165f611b08565b815f52673ec412a9852d173d60c11b601c5260205f208201820180548060a01c831860a01b8118825550505050565b60606080604051019050602081016040525f8152805f19835b928101926030600a8206018453600a9004806119bf575050819003601f19909101908152919050565b60608351835183516020870196506020860195506020850194506020604051019350828701838311611ab8576001838203015f60208510611a2857508388205b601f851660200360031b89515b8b51818118831c611a9f578315611a6f5783888e2014611a6f57808a5260019c8d019c90990198848d10611a695750611ab3565b50611a35565b5f5b8b8101518b820152602001878110611a7157509b87019b988601988715611a9f57848d10611a695750611ab3565b895260019b8c019b90980197838c10611a35575b505050505b84935060206040510194508781038585030192505b80881015611ae8578751845260209788019790930192611acd565b50505f818401908152602001604052601f19909201918252509392505050565b604051856014820152635194544760601b81525f6034820152608060548201525f60748201528460988201528360b88201528260d88201528160f8820152606460948201525f38610124601084015f8b5af1611b66573d5f823e3d81fd5b50505050505050565b638b78c6d8600c52825f526020600c20805483811783611b90575080841681185b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f80a3505050505050565b611bd15f8484611717565b8260601b60601c9250815f52673ec412a9852d173d60c11b601c52828160a01b1760205f208301830155825f52601c600c20600181540163ffffffff81168502611c2a5767ea553b3401336cea851560021b526004601cfd5b905581835f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8138a4505050565b5f6040518660148201528560348201528460548201528360601b60601c6074820152826094820152638a54c52f60601b815260205f60a4601084015f6f6551c19487814612e58fe068137757585af1601f3d1116611cbd576320188a595f526004601cfd5b50505f5195945050505050565b673ec412a9852d173d60c11b601c52815f52601c600c2080548060201c831860201b8118825550505050565b5f60208284031215611d06575f80fd5b81356001600160e01b03198116811461112a575f80fd5b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b80356001600160a01b0381168114610fd1575f80fd5b5f60208284031215611d78575f80fd5b61112a82611d52565b5f60208284031215611d91575f80fd5b5035919050565b5f8060408385031215611da9575f80fd5b611db283611d52565b946020939093013593505050565b5f8083601f840112611dd0575f80fd5b5081356001600160401b03811115611de6575f80fd5b6020830191508360208260051b8501011115611e00575f80fd5b9250929050565b5f805f60408486031215611e19575f80fd5b611e2284611d52565b925060208401356001600160401b03811115611e3c575f80fd5b611e4886828701611dc0565b9497909650939450505050565b5f805f60608486031215611e67575f80fd5b611e7084611d52565b9250611e7e60208501611d52565b9150604084013590509250925092565b5f8083601f840112611e9e575f80fd5b5081356001600160401b03811115611eb4575f80fd5b602083019150836020828501011115611e00575f80fd5b5f8060208385031215611edc575f80fd5b82356001600160401b03811115611ef1575f80fd5b611efd85828601611e8e565b90969095509350505050565b5f805f8060408587031215611f1c575f80fd5b84356001600160401b0380821115611f32575f80fd5b611f3e88838901611e8e565b90965094506020870135915080821115611f56575f80fd5b50611f6387828801611e8e565b95989497509550505050565b80358015158114610fd1575f80fd5b5f8060408385031215611f8f575f80fd5b611f9883611d52565b9150611fa660208401611f6f565b90509250929050565b5f8060208385031215611fc0575f80fd5b82356001600160401b03811115611fd5575f80fd5b611efd85828601611dc0565b5f60208284031215611ff1575f80fd5b61112a82611f6f565b5f805f805f6080868803121561200e575f80fd5b61201786611d52565b945061202560208701611d52565b93506040860135925060608601356001600160401b03811115612046575f80fd5b61205288828901611e8e565b969995985093965092949392505050565b5f8060408385031215612074575f80fd5b61207d83611d52565b9150611fa660208401611d52565b600181811c9082168061209f57607f821691505b60208210810361142d57634e487b7160e01b5f52602260045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b601f821115610d0057805f5260205f20601f840160051c810160208510156120f65750805b601f840160051c820191505b8181101561126d575f8155600101612102565b6001600160401b0383111561212c5761212c6120bd565b6121408361213a835461208b565b836120d1565b5f601f841160018114612171575f851561215a5750838201355b5f19600387901b1c1916600186901b17835561126d565b5f83815260208120601f198716915b828110156121a05786850135825560209485019460019092019101612180565b50868210156121bc575f1960f88860031b161c19848701351681555b505060018560011b018355505050505056fea26469706673582212207b184e74ca114f5efbf54d0fbe4087ef6dba4ce52b7914fe01c91add502a40dc64736f6c63430008190033
Deployed Bytecode Sourcemap
163:2287:6:-: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:7;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;20241:126::-;;;;;;;;;;-1:-1:-1;20241:126:7;;;;;:::i;:::-;;:::i;:::-;;;-1:-1:-1;;;;;1453:32:8;;;1435:51;;1423:2;1408:18;20241:126:7;1289:203:8;8099:532:3;;;;;;;;;;-1:-1:-1;8099:532:3;;;;;:::i;:::-;;:::i;8925:119::-;;;;;;:::i;:::-;;:::i;:::-;;10759:489:7;;;;;;;;;;-1:-1:-1;10759:489:7;;;;;:::i;:::-;;:::i;5772:96::-;;;;;;;;;;-1:-1:-1;5850:11:7;;-1:-1:-1;;;;;5850:11:7;5772:96;;;2975:25:8;;;2963:2;2948:18;5772:96:7;2829:177:8;10127:109:2;;;;;;:::i;:::-;;:::i;7842:137:7:-;;;;;;;;;;-1:-1:-1;7842:137:7;;;;;:::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;10748:2886:3;;;;;;:::i;:::-;;:::i;9021:617:1:-;;;:::i;9138:415:7:-;;;;;;;;;;-1:-1:-1;9138:415:7;;;;;:::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:7;;;;;;;;;;;;14166:7:2;1064:55:7;;21010:407;;;;;;;;;;-1:-1:-1;21010:407:7;;;;;:::i;:::-;;:::i;2303:145:6:-;;;;;;;;;;-1:-1:-1;2303:145:6;;;;;:::i;:::-;;:::i;10512:152:7:-;;;;;;;;;;-1:-1:-1;10512:152:7;;;;;:::i;:::-;;:::i;13705:198:3:-;;;;;;:::i;:::-;;:::i;9856:125:2:-;;;;;;:::i;:::-;;:::i;11786:359:7:-;;;;;;;;;;-1:-1:-1;11786:359:7;;;;;:::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;9720:456:1;;;:::i;6199:118:7:-;;;;;;;;;;-1:-1:-1;6199:118:7;;;;;:::i;:::-;;:::i;6564:197::-;;;;;;;;;;-1:-1:-1;6564:197:7;;;;;:::i;:::-;;:::i;8035:113::-;;;;;;;;;;;;;:::i;6957:332:3:-;;;;;;;;;;-1:-1:-1;6957:332:3;;;;;:::i;:::-;;:::i;7433:533::-;;;;;;;;;;-1:-1:-1;7433:533:3;;;;;:::i;:::-;;:::i;8762:100:1:-;;;:::i;848:25:6:-;;;;;;;;;;-1:-1:-1;848:25:6;;;;-1:-1:-1;;;848:25:6;;-1:-1:-1;;;;;848:25:6;;;21536:140:7;;;;;;;;;;-1:-1:-1;21536:140:7;;;;;:::i;:::-;;:::i;3183:25::-;;;;;;;;;;-1:-1:-1;3183:25:7;;;;-1:-1:-1;;;;;3183:25:7;;;;;;-1:-1:-1;;;;;5001:31:8;;;4983:50;;4971:2;4956:18;3183:25:7;4839:200:8;11408:182:1;;;;;;;;;;-1:-1:-1;;;11556:18:1;11408:182;;790:76:7;;;;;;;;;;;;824:42;790:76;;924:83;;;;;;;;;;;;965:42;924:83;;4142:102;;;;;;;;;;;;;:::i;10032:382::-;;;;;;;;;;-1:-1:-1;10032:382:7;;;;;:::i;:::-;;:::i;9667:726:3:-;;;;;;;;;;-1:-1:-1;9667:726:3;;;;;:::i;:::-;;:::i;21760:134:7:-;;;;;;;;;;-1:-1:-1;21760:134:7;;;;;:::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:7;;;21760:134;930:28:6;;;;;;;;;;-1:-1:-1;930:28:6;;;;-1:-1:-1;;;930:28:6;;;;;;11349:209:7;;;;;;;;;;-1:-1:-1;11349:209:7;;;;;:::i;:::-;;:::i;21962:571::-;;;;;;;;;;-1:-1:-1;21962:571:7;;;;;:::i;:::-;;:::i;8807:151::-;;;;;;;;;;-1:-1:-1;8807:151:7;;;;;:::i;:::-;;:::i;2171:91:6:-;;;;;;;;;;;;;:::i;14406:249:3:-;;;;;;:::i;:::-;;:::i;20488:411:7:-;;;;;;;;;;-1:-1:-1;20488:411:7;;;;;:::i;:::-;;:::i;4350:1382::-;;;;;;;;;;-1:-1:-1;4350:1382:7;;;;;:::i;:::-;;:::i;9732:208::-;;;;;;;;;;-1:-1:-1;9732:208:7;;;;;:::i;:::-;;:::i;9135:392:3:-;;;;;;;;;;-1:-1:-1;9135:392:3;;;;;:::i;:::-;9355:4;9348:22;9396:31;9390:4;9383:45;9255:11;9441:19;9505:4;9499;9489:21;9483:28;;9135:392;10363:708:1;;;;;;:::i;:::-;;:::i;8348:349::-;;;;;;:::i;:::-;;:::i;8325:308:7:-;;;;;;;;;;-1:-1:-1;8325:308:7;;;;;:::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;6374:138:7;;;;;;;;;;-1:-1:-1;6374:138:7;;;;;:::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:7:o;8099:532:3:-;8161:14;8266:2;8260:4;8253:16;-1:-1:-1;;;8289:4:3;8282:38;8386:4;8380;8370:21;8366:2;8362:30;8358:2;8354:39;8430:13;8424:20;8420:2;8416:29;8406:158;;8478:10;8472:4;8465:24;8545:4;8539;8532:18;8406:158;8597:1;8593:21;8587:28;;8099:532;-1:-1:-1;;8099:532:3:o;8925:119::-;9004:33;9013:10;9025:7;9034:2;9004:8;:33::i;:::-;8925:119;;:::o;10759:489:7:-;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:7::1;10953:10;10945:7;:19::i;:::-;10937:27:::0;-1:-1:-1;;;;;;11004:19:7;::::1;11000:56;;11032:24;;-1:-1:-1::0;;;11032:24:7::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;2021:1:5::0;1997:22;1990:33;10759:489:7;;;;;:::o;10127:109:2:-;10198:31;10211:10;10223:5;10198:12;:31::i;:::-;10127:109;:::o;7842:137:7:-;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:7::1;7965:6;7952:12;:20::i;:::-;7944:28;;2021:1:5::0;1997:22;1990:33;7842:137:7;;;:::o;9588:123:2:-;12517:13:1;:11;:13::i;:::-;9680:24:2::1;9692:4;9698:5;9680:11;:24::i;10748:2886:3:-:0;10841:34;10862:4;10868:2;10872;10841:20;:34::i;:::-;11025:1;11169:16;;;-1:-1:-1;;;11240:8:3;11211:38;11205:4;11198:52;11316:4;11300:21;;11292:30;;11284:39;;11359:20;;-1:-1:-1;;;;;11049:25:3;;;;11093:23;;;;11405:36;;;11284:39;11557:15;;;11546:27;;11536:229;;11697:18;11688:5;11681:13;11678:1;11674:21;11667:49;11746:4;11740;11733:18;11536:229;11868:4;11862;11855:18;11926:13;11923:1;11919:21;11913:28;11890:51;;12076:15;12066:8;12063:29;12056:4;12046:8;12043:18;12040:53;12030:288;;12149:4;12143;12133:21;12127:28;12117:183;;12196:10;12190:4;12183:24;12273:4;12267;12260:18;12117:183;12393:15;12390:55;;;12441:1;12425:13;12422:1;12418:21;12411:32;12390:55;12557:13;;;12536:35;12514:58;;-1:-1:-1;12690:4:3;12684;12674:21;;;12740:22;;-1:-1:-1;;12736:30:3;12712:55;;-1:-1:-1;12858:16:3;;;12912:21;12981:20;;12764:1;12977:28;13157:20;13132:46;;13124:55;;13114:270;;13308:18;13302:2;13295:10;13292:1;13288:18;13281:46;13361:4;13355;13348:18;13114:270;13401:42;;13572:2;13568;13562:4;13535:25;13529:4;13517:10;13512:63;13594:33;10748:2886;;;:::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:7:-;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:7::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;10127:109:2::0;21010:407:7;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:7;:22;21369:13;21361:7;:22::i;:::-;-1:-1:-1;;;;;21361:31:7;;21357:53;;21403:7;21394:16;;21357:53;21092:325;;21010:407;;;:::o;2303:145:6:-;12517:13:1;:11;:13::i;:::-;2380:16:6::1;::::0;-1:-1:-1;;;2380:16:6;::::1;;;2376:37;;;2405:8;;-1:-1:-1::0;;;2405:8:6::1;;;;;;;;;;;2376:37;2423:10;:18:::0;;-1:-1:-1;;;;;2423:18:6;;::::1;-1:-1:-1::0;;;2423:18:6::1;-1:-1:-1::0;;;;;;2423:18:6;;::::1;::::0;;;::::1;::::0;;2303:145::o;10512:152:7:-;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;11786:359:7:-:0;11921:7;11944:13;11960:15;11968:6;11960:7;:15::i;:::-;11944:31;-1:-1:-1;;;;;;11989:19:7;;11985:33;;-1:-1:-1;12017:1:7;;11786:359;-1:-1:-1;;11786:359:7: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:7: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:7:-;14166:7:2;11991:25;12010:5;11991:18;:25::i;:::-;6296:8:7::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:7::1;:13;6722:5:::0;;6714;:13:::1;:::i;:::-;-1:-1:-1::0;6737:7:7::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;8762:100:1:-;12517:13;:11;:13::i;:::-;8834:21:::1;8852:1;8834:9;:21::i;:::-;8762:100::o:0;21536:140:7:-;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;10032:382::-;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:7::1;10206:10;10198:7;:19::i;:::-;10190:27:::0;-1:-1:-1;;;;;;10257:19:7;::::1;10253:56;;10285:24;;-1:-1:-1::0;;;10285:24:7::1;;;;;;;;;;;10253:56;10319:55;10347:5;824:42;10363:2;10367:6;10319:27;:55::i;:::-;2021:1:5::0;1997:22;1990:33;10032:382:7;;;;:::o;9667:726:3:-;9882:10;9875:18;9868:26;9854:40;;9991:8;9985:4;9978:22;10026:31;10020:4;10013:45;10084:8;10078:4;10071:22;10136:10;10129:4;10123;10113:21;10106:41;10221:10;10215:4;10208:24;10366:8;10362:2;10358:17;10354:2;10350:26;10340:8;10305:33;10299:4;10293;10288:89;9667:726;;:::o;11349:209:7:-;11463:13;11500:51;11530:10;11542:8;;11500:29;:51::i;:::-;11492:59;11349:209;-1:-1:-1;;;11349:209:7:o;21962:571::-;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;22048:13:7::1;22064:19;22072:10;22064:7;:19::i;:::-;22048:35:::0;-1:-1:-1;;;;;;22097:19:7;::::1;22093:56;;22125:24;;-1:-1:-1::0;;;22125:24:7::1;;;;;;;;;;;22093:56;22293:15;22311:25;22330:5;22311:18;:25::i;:::-;22293:43:::0;-1:-1:-1;22421:48:7::1;22293:43:::0;24180:9;;24173:17;22421:13:::1;:48::i;:::-;22509:7;22484:42;22518:7;22484:42;;;;470:14:8::0;463:22;445:41;;433:2;418:18;;305:187;22484:42:7::1;;;;;;;;22038:495;;2021:1:5::0;1997:22;1990:33;21962:571:7;:::o;8807:151::-;8876:13;8909:42;8932:10;8944:6;8909:22;:42::i;2171:91:6:-;12517:13:1;:11;:13::i;:::-;2232:16:6::1;:23:::0;;-1:-1:-1;;;;2232:23:6::1;-1:-1:-1::0;;;2232:23:6::1;::::0;;2171:91::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:7:-;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:7;:22;20845:13;20837:7;:22::i;:::-;-1:-1:-1;;;;;20837:31:7;;20833:59;;20879:13;20488:411;-1:-1:-1;;;20488:411:7: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:7;4450:44;;4475:19;;-1:-1:-1;;;4475:19:7;;;;;;;;;;;4450:44;4505:16;4524:17;-1:-1:-1;;;;;4524:17:7;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:7;;5656:59;5674:8;5656:59;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5656:59:7;;;5692:22;5711:2;5692:18;:22::i;:::-;5656:17;:59::i;:::-;5647:68;;5600:126;4440:1292;4350:1382;;;:::o;9732:208::-;9845:13;9882:51;9912:10;9924:8;;9882:29;:51::i;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;8325:308:7:-:0;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:7::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;10127:109:2::0;6374:138:7;14166:7:2;11991:25;12010:5;11991:18;:25::i;:::-;-1:-1:-1;6477:17:7::1;:28:::0;;-1:-1:-1;;;;;;6477:28:7::1;-1:-1:-1::0;;;;;6477:28:7;;;::::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;29039:1442::-;29222:1;29218:6;29214:2;29210:15;29269:7;29253:14;29249:28;29238:39;;29316:2;29300:14;29296:23;29290:29;;29389:2;29383:4;29376:16;29447:2;-1:-1:-1;;;29418:32:3;29412:4;29405:46;29517:4;29511;29501:21;29497:2;29493:30;29489:2;29485:39;29576:13;29570:20;29554:14;29550:41;29537:54;;29665:5;29655:134;;29703:10;29697:4;29690:24;29770:4;29764;29757:18;29655:134;29972:5;29968:2;29965:13;29960:2;29953:10;29950:29;29940:280;;30012:5;30006:4;29999:19;30067:4;30061;30051:21;30045:28;30035:171;;30110:10;30104:4;30097:24;30183:4;30177;30170:18;30035:171;30314:1;30310:21;30303:38;;;30462:2;30333:7;30446:5;30419:25;30413:4;30401:10;30396:69;;29039:1442;;;:::o;17773:309:7:-;17996:79;18005:5;18012:12;18026:10;18046:5;-1:-1:-1;;;;;17996:79:7;18062:2;-1:-1:-1;;;;;17996:79:7;18067:7;17996:8;:79::i;4304:117:2:-;4382:32;4395:4;4401:5;4408;4382:12;:32::i;12689:1254:7:-;12752:13;12801:14;12809:5;12801:7;:14::i;:::-;12777:40;-1:-1:-1;;;;;;12831:19:7;;;12827:37;;12689:1254;;;:::o;12827:37::-;1844:10:6;;-1:-1:-1;;;1844:10:6;;-1:-1:-1;;;;;1844:10:6;;12941:57:7;;12973:25;;-1:-1:-1;;;12973:25:7;;;;;;;;;;;12941:57;13032:11;13030:13;;-1:-1:-1;;;;;13030:13:7;;;;;;-1:-1:-1;;13030:13:7;;;;;;;;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:7;13775:7;13768:5;-1:-1:-1;;;;;13755:35:7;;;;;;;;;;;13896:30;13904:5;13919;-1:-1:-1;;;;;13896:30:7;: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;22976:554:7:-;23125:11;23133:2;23125:7;:11::i;:::-;-1:-1:-1;;;;;23125:16:7;;23121:53;;23150:24;;-1:-1:-1;;;23150:24:7;;;;;;;;;;;23121:53;-1:-1:-1;;;;;23188:18:7;;;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:7;;23222:66;;23262:26;;-1:-1:-1;;;23262:26:7;;;;;;;;;;;23222:66;23302:25;23316:7;23325:1;23302:13;:25::i;:::-;23385:26;23393:2;23397:13;23405:4;23397:7;:13::i;:::-;23385:7;:26::i;:::-;23460:16;23468:4;23474:1;23460:7;:16::i;16623:989::-;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:7;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:7: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:7;19458:14;19563:7;19557:4;19550:21;-1:-1:-1;;;19624:4:7;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;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;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:7:-;16132:71;16141:5;16148:9;16159:10;16179:2;-1:-1:-1;;;;;16132:71:7;16192:6;-1:-1:-1;;;;;16132:71:7;16201:1;16132:8;:71::i;18689:432:3:-;18844:2;18838:4;18831:16;-1:-1:-1;;;18867:4:3;18860:38;18964:4;18958;18948:21;18944:2;18940:30;18936:2;18932:39;19004:13;18998:20;19094:6;19089:3;19085:16;19078:5;19074:28;19069:3;19065:38;19057:6;19053:51;19038:13;19031:74;;;18689:432;;:::o;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:7:-;18502:4;18496:11;18541:6;18534:4;18531:1;18527:12;18520:28;-1:-1:-1;;;18568:1:7;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;10748:2886;;;:::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;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:367::-;2004:8;2014:6;2068:3;2061:4;2053:6;2049:17;2045:27;2035:55;;2086:1;2083;2076:12;2035:55;-1:-1:-1;2109:20:8;;-1:-1:-1;;;;;2141:30:8;;2138:50;;;2184:1;2181;2174:12;2138:50;2221:4;2213:6;2209:17;2197:29;;2281:3;2274:4;2264:6;2261:1;2257:14;2249:6;2245:27;2241:38;2238:47;2235:67;;;2298:1;2295;2288:12;2235:67;1941:367;;;;;:::o;2313:511::-;2408:6;2416;2424;2477:2;2465:9;2456:7;2452:23;2448:32;2445:52;;;2493:1;2490;2483:12;2445:52;2516:29;2535:9;2516:29;:::i;:::-;2506:39;;2596:2;2585:9;2581:18;2568:32;-1:-1:-1;;;;;2615:6:8;2612:30;2609:50;;;2655:1;2652;2645:12;2609:50;2694:70;2756:7;2747:6;2736:9;2732:22;2694:70;:::i;:::-;2313:511;;2783:8;;-1:-1:-1;2668:96:8;;-1:-1:-1;;;;2313:511:8:o;3011:328::-;3088:6;3096;3104;3157:2;3145:9;3136:7;3132:23;3128:32;3125:52;;;3173:1;3170;3163:12;3125:52;3196:29;3215:9;3196:29;:::i;:::-;3186:39;;3244:38;3278:2;3267:9;3263:18;3244:38;:::i;:::-;3234:48;;3329:2;3318:9;3314:18;3301:32;3291:42;;3011:328;;;;;:::o;3344:348::-;3396:8;3406:6;3460:3;3453:4;3445:6;3441:17;3437:27;3427:55;;3478:1;3475;3468:12;3427:55;-1:-1:-1;3501:20:8;;-1:-1:-1;;;;;3533:30:8;;3530:50;;;3576:1;3573;3566:12;3530:50;3613:4;3605:6;3601:17;3589:29;;3665:3;3658:4;3649:6;3641;3637:19;3633:30;3630:39;3627:59;;;3682:1;3679;3672:12;3697:411;3768:6;3776;3829:2;3817:9;3808:7;3804:23;3800:32;3797:52;;;3845:1;3842;3835:12;3797:52;3885:9;3872:23;-1:-1:-1;;;;;3910:6:8;3907:30;3904:50;;;3950:1;3947;3940:12;3904:50;3989:59;4040:7;4031:6;4020:9;4016:22;3989:59;:::i;:::-;4067:8;;3963:85;;-1:-1:-1;3697:411:8;-1:-1:-1;;;;3697:411:8:o;4113:721::-;4205:6;4213;4221;4229;4282:2;4270:9;4261:7;4257:23;4253:32;4250:52;;;4298:1;4295;4288:12;4250:52;4338:9;4325:23;-1:-1:-1;;;;;4408:2:8;4400:6;4397:14;4394:34;;;4424:1;4421;4414:12;4394:34;4463:59;4514:7;4505:6;4494:9;4490:22;4463:59;:::i;:::-;4541:8;;-1:-1:-1;4437:85:8;-1:-1:-1;4629:2:8;4614:18;;4601:32;;-1:-1:-1;4645:16:8;;;4642:36;;;4674:1;4671;4664:12;4642:36;;4713:61;4766:7;4755:8;4744:9;4740:24;4713:61;:::i;:::-;4113:721;;;;-1:-1:-1;4793:8:8;-1:-1:-1;;;;4113:721:8:o;5044:160::-;5109:20;;5165:13;;5158:21;5148:32;;5138:60;;5194:1;5191;5184:12;5209:254;5274:6;5282;5335:2;5323:9;5314:7;5310:23;5306:32;5303:52;;;5351:1;5348;5341:12;5303:52;5374:29;5393:9;5374:29;:::i;:::-;5364:39;;5422:35;5453:2;5442:9;5438:18;5422:35;:::i;:::-;5412:45;;5209:254;;;;;:::o;5468:437::-;5554:6;5562;5615:2;5603:9;5594:7;5590:23;5586:32;5583:52;;;5631:1;5628;5621:12;5583:52;5671:9;5658:23;-1:-1:-1;;;;;5696:6:8;5693:30;5690:50;;;5736:1;5733;5726:12;5690:50;5775:70;5837:7;5828:6;5817:9;5813:22;5775:70;:::i;5910:180::-;5966:6;6019:2;6007:9;5998:7;5994:23;5990:32;5987:52;;;6035:1;6032;6025:12;5987:52;6058:26;6074:9;6058:26;:::i;6095:627::-;6192:6;6200;6208;6216;6224;6277:3;6265:9;6256:7;6252:23;6248:33;6245:53;;;6294:1;6291;6284:12;6245:53;6317:29;6336:9;6317:29;:::i;:::-;6307:39;;6365:38;6399:2;6388:9;6384:18;6365:38;:::i;:::-;6355:48;;6450:2;6439:9;6435:18;6422:32;6412:42;;6505:2;6494:9;6490:18;6477:32;-1:-1:-1;;;;;6524:6:8;6521:30;6518:50;;;6564:1;6561;6554:12;6518:50;6603:59;6654:7;6645:6;6634:9;6630:22;6603:59;:::i;:::-;6095:627;;;;-1:-1:-1;6095:627:8;;-1:-1:-1;6681:8:8;;6577:85;6095:627;-1:-1:-1;;;6095:627:8:o;6727:260::-;6795:6;6803;6856:2;6844:9;6835:7;6831:23;6827:32;6824:52;;;6872:1;6869;6862:12;6824:52;6895:29;6914:9;6895:29;:::i;:::-;6885:39;;6943:38;6977:2;6966:9;6962:18;6943:38;:::i;6992:380::-;7071:1;7067:12;;;;7114;;;7135:61;;7189:4;7181:6;7177:17;7167:27;;7135:61;7242:2;7234:6;7231:14;7211:18;7208:38;7205:161;;7288:10;7283:3;7279:20;7276:1;7269:31;7323:4;7320:1;7313:15;7351:4;7348:1;7341:15;7377:127;7438:10;7433:3;7429:20;7426:1;7419:31;7469:4;7466:1;7459:15;7493:4;7490:1;7483:15;7635:518;7737:2;7732:3;7729:11;7726:421;;;7773:5;7770:1;7763:16;7817:4;7814:1;7804:18;7887:2;7875:10;7871:19;7868:1;7864:27;7858:4;7854:38;7923:4;7911:10;7908:20;7905:47;;;-1:-1:-1;7946:4:8;7905:47;8001:2;7996:3;7992:12;7989:1;7985:20;7979:4;7975:31;7965:41;;8056:81;8074:2;8067:5;8064:13;8056:81;;;8133:1;8119:16;;8100:1;8089:13;8056:81;;8329:1198;-1:-1:-1;;;;;8448:3:8;8445:27;8442:53;;;8475:18;;:::i;:::-;8504:94;8594:3;8554:38;8586:4;8580:11;8554:38;:::i;:::-;8548:4;8504:94;:::i;:::-;8624:1;8649:2;8644:3;8641:11;8666:1;8661:608;;;;9313:1;9330:3;9327:93;;;-1:-1:-1;9386:19:8;;;9373:33;9327:93;-1:-1:-1;;8286:1:8;8282:11;;;8278:24;8274:29;8264:40;8310:1;8306:11;;;8261:57;9433:78;;8634:887;;8661:608;7582:1;7575:14;;;7619:4;7606:18;;-1:-1:-1;;8697:17:8;;;8812:229;8826:7;8823:1;8820:14;8812:229;;;8915:19;;;8902:33;8887:49;;9022:4;9007:20;;;;8975:1;8963:14;;;;8842:12;8812:229;;;8816:3;9069;9060:7;9057:16;9054:159;;;9193:1;9189:6;9183:3;9177;9174:1;9170:11;9166:21;9162:34;9158:39;9145:9;9140:3;9136:19;9123:33;9119:79;9111:6;9104:95;9054:159;;;9256:1;9250:3;9247:1;9243:11;9239:19;9233:4;9226:33;8634:887;;8329:1198;;;:::o
Swarm Source
ipfs://7b184e74ca114f5efbf54d0fbe4087ef6dba4ce52b7914fe01c91add502a40dc
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.