Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00
Cross-Chain Transactions
Loading...
Loading
Contract Name:
AccountExtension
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 20 runs
Other Settings:
london EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.11;
/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-inline-assembly */
/* solhint-disable reason-string */
// Extensions
import "../../../extension/upgradeable/AccountPermissions.sol";
import "../../../extension/upgradeable/ContractMetadata.sol";
import "../../../external-deps/openzeppelin/token/ERC721/utils/ERC721Holder.sol";
import "../../../external-deps/openzeppelin/token/ERC1155/utils/ERC1155Holder.sol";
// Utils
import "../../../eip/ERC1271.sol";
import "../../../external-deps/openzeppelin/utils/cryptography/ECDSA.sol";
import "../../../external-deps/openzeppelin/utils/structs/EnumerableSet.sol";
import "./BaseAccountFactory.sol";
import "./AccountCore.sol";
import "./AccountCoreStorage.sol";
// $$\ $$\ $$\ $$\ $$\
// $$ | $$ | \__| $$ | $$ |
// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\
// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\
// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ |
// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ |
// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ |
// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/
contract AccountExtension is ContractMetadata, ERC1271, AccountPermissions, ERC721Holder, ERC1155Holder {
using ECDSA for bytes32;
using EnumerableSet for EnumerableSet.AddressSet;
bytes32 private constant MSG_TYPEHASH = keccak256("AccountMessage(bytes message)");
/*///////////////////////////////////////////////////////////////
Constructor, Initializer, Modifiers
//////////////////////////////////////////////////////////////*/
/// @notice Checks whether the caller is the EntryPoint contract or the admin.
modifier onlyAdminOrEntrypoint() virtual {
require(
msg.sender == address(AccountCore(payable(address(this))).entryPoint()) || isAdmin(msg.sender),
"Account: not admin or EntryPoint."
);
_;
}
// solhint-disable-next-line no-empty-blocks
receive() external payable virtual {}
constructor() EIP712("Account", "1") {}
/*///////////////////////////////////////////////////////////////
View functions
//////////////////////////////////////////////////////////////*/
/// @notice See {IERC165-supportsInterface}.
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155Receiver) returns (bool) {
return
interfaceId == type(IERC1155Receiver).interfaceId ||
interfaceId == type(IERC721Receiver).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @notice See EIP-1271
*
* @param _hash The original message hash of the data to sign (before mixing this contract's domain separator)
* @param _signature The signature produced on signing the typed data hash (result of `getMessageHash(abi.encode(rawData))`)
*/
function isValidSignature(
bytes32 _hash,
bytes memory _signature
) public view virtual override returns (bytes4 magicValue) {
bytes32 targetDigest = getMessageHash(_hash);
address signer = targetDigest.recover(_signature);
if (isAdmin(signer)) {
return MAGICVALUE;
}
address caller = msg.sender;
EnumerableSet.AddressSet storage approvedTargets = _accountPermissionsStorage().approvedTargets[signer];
require(
approvedTargets.contains(caller) || (approvedTargets.length() == 1 && approvedTargets.at(0) == address(0)),
"Account: caller not approved target."
);
if (isActiveSigner(signer)) {
magicValue = MAGICVALUE;
}
}
/**
* @notice Returns the hash of message that should be signed for EIP1271 verification.
* @param _hash The message hash to sign for the EIP-1271 origin verifying contract.
* @return messageHash The digest to sign for EIP-1271 verification.
*/
function getMessageHash(bytes32 _hash) public view returns (bytes32) {
bytes32 messageHash = keccak256(abi.encode(_hash));
bytes32 typedDataHash = keccak256(abi.encode(MSG_TYPEHASH, messageHash));
return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), typedDataHash));
}
/*///////////////////////////////////////////////////////////////
External functions
//////////////////////////////////////////////////////////////*/
/// @notice Executes a transaction (called directly from an admin, or by entryPoint)
function execute(address _target, uint256 _value, bytes calldata _calldata) external virtual onlyAdminOrEntrypoint {
_registerOnFactory();
_call(_target, _value, _calldata);
}
/// @notice Executes a sequence transaction (called directly from an admin, or by entryPoint)
function executeBatch(
address[] calldata _target,
uint256[] calldata _value,
bytes[] calldata _calldata
) external virtual onlyAdminOrEntrypoint {
_registerOnFactory();
require(_target.length == _calldata.length && _target.length == _value.length, "Account: wrong array lengths.");
for (uint256 i = 0; i < _target.length; i++) {
_call(_target[i], _value[i], _calldata[i]);
}
}
/// @notice Deposit funds for this account in Entrypoint.
function addDeposit() public payable {
AccountCore(payable(address(this))).entryPoint().depositTo{ value: msg.value }(address(this));
}
/// @notice Withdraw funds for this account from Entrypoint.
function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public {
_onlyAdmin();
AccountCore(payable(address(this))).entryPoint().withdrawTo(withdrawAddress, amount);
}
/*///////////////////////////////////////////////////////////////
Internal functions
//////////////////////////////////////////////////////////////*/
/// @dev Registers the account on the factory if it hasn't been registered yet.
function _registerOnFactory() internal virtual {
address factory = AccountCore(payable(address(this))).factory();
BaseAccountFactory factoryContract = BaseAccountFactory(factory);
if (!factoryContract.isRegistered(address(this))) {
factoryContract.onRegister(AccountCoreStorage.data().creationSalt);
}
}
/// @dev Calls a target contract and reverts if it fails.
function _call(address _target, uint256 value, bytes memory _calldata) internal returns (bytes memory result) {
bool success;
(success, result) = _target.call{ value: value }(_calldata);
if (!success) {
assembly {
revert(add(result, 32), mload(result))
}
}
}
/// @dev Returns whether contract metadata can be set in the given execution context.
function _canSetContractURI() internal view virtual override returns (bool) {
return isAdmin(msg.sender) || msg.sender == address(this);
}
function _afterSignerPermissionsUpdate(SignerPermissionRequest calldata _req) internal virtual override {}
}// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.0;
abstract contract ERC1271 {
// bytes4(keccak256("isValidSignature(bytes32,bytes)")
bytes4 internal constant MAGICVALUE = 0x1626ba7e;
/**
* @dev Should return whether the signature provided is valid for the provided hash
* @param _hash Hash of the data to be signed
* @param _signature Signature byte array associated with _hash
*
* MUST return the bytes4 magic value 0x1626ba7e when function passes.
* MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
* MUST allow external calls
*/
function isValidSignature(bytes32 _hash, bytes memory _signature) public view virtual returns (bytes4 magicValue);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./interface/IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* [EIP](https://eips.ethereum.org/EIPS/eip-165).
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.0;
/// @author thirdweb
import "../lib/Address.sol";
import "./interface/IMulticall.sol";
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* _Available since v4.1._
*/
contract Multicall is IMulticall {
/**
* @notice Receives and executes a batch of function calls on this contract.
* @dev Receives and executes a batch of function calls on this contract.
*
* @param data The bytes data that makes up the batch of function calls to execute.
* @return results The bytes data that makes up the result of the batch of function calls executed.
*/
function multicall(bytes[] calldata data) external returns (bytes[] memory results) {
results = new bytes[](data.length);
address sender = _msgSender();
bool isForwarder = msg.sender != sender;
for (uint256 i = 0; i < data.length; i++) {
if (isForwarder) {
results[i] = Address.functionDelegateCall(address(this), abi.encodePacked(data[i], sender));
} else {
results[i] = Address.functionDelegateCall(address(this), data[i]);
}
}
return results;
}
/// @notice Returns the sender in the given execution context.
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @author thirdweb
interface IAccountPermissions {
/*///////////////////////////////////////////////////////////////
Types
//////////////////////////////////////////////////////////////*/
/**
* @notice The payload that must be signed by an authorized wallet to set permissions for a signer to use the smart wallet.
*
* @param signer The addres of the signer to give permissions.
* @param approvedTargets The list of approved targets that a role holder can call using the smart wallet.
* @param nativeTokenLimitPerTransaction The maximum value that can be transferred by a role holder in a single transaction.
* @param permissionStartTimestamp The UNIX timestamp at and after which a signer has permission to use the smart wallet.
* @param permissionEndTimestamp The UNIX timestamp at and after which a signer no longer has permission to use the smart wallet.
* @param reqValidityStartTimestamp The UNIX timestamp at and after which a signature is valid.
* @param reqValidityEndTimestamp The UNIX timestamp at and after which a signature is invalid/expired.
* @param uid A unique non-repeatable ID for the payload.
* @param isAdmin Whether the signer should be an admin.
*/
struct SignerPermissionRequest {
address signer;
uint8 isAdmin;
address[] approvedTargets;
uint256 nativeTokenLimitPerTransaction;
uint128 permissionStartTimestamp;
uint128 permissionEndTimestamp;
uint128 reqValidityStartTimestamp;
uint128 reqValidityEndTimestamp;
bytes32 uid;
}
/**
* @notice The permissions that a signer has to use the smart wallet.
*
* @param signer The address of the signer.
* @param approvedTargets The list of approved targets that a role holder can call using the smart wallet.
* @param nativeTokenLimitPerTransaction The maximum value that can be transferred by a role holder in a single transaction.
* @param startTimestamp The UNIX timestamp at and after which a signer has permission to use the smart wallet.
* @param endTimestamp The UNIX timestamp at and after which a signer no longer has permission to use the smart wallet.
*/
struct SignerPermissions {
address signer;
address[] approvedTargets;
uint256 nativeTokenLimitPerTransaction;
uint128 startTimestamp;
uint128 endTimestamp;
}
/**
* @notice Internal struct for storing permissions for a signer (without approved targets).
*
* @param nativeTokenLimitPerTransaction The maximum value that can be transferred by a role holder in a single transaction.
* @param startTimestamp The UNIX timestamp at and after which a signer has permission to use the smart wallet.
* @param endTimestamp The UNIX timestamp at and after which a signer no longer has permission to use the smart wallet.
*/
struct SignerPermissionsStatic {
uint256 nativeTokenLimitPerTransaction;
uint128 startTimestamp;
uint128 endTimestamp;
}
/*///////////////////////////////////////////////////////////////
Events
//////////////////////////////////////////////////////////////*/
/// @notice Emitted when permissions for a signer are updated.
event SignerPermissionsUpdated(
address indexed authorizingSigner,
address indexed targetSigner,
SignerPermissionRequest permissions
);
/// @notice Emitted when an admin is set or removed.
event AdminUpdated(address indexed signer, bool isAdmin);
/*///////////////////////////////////////////////////////////////
View functions
//////////////////////////////////////////////////////////////*/
/// @notice Returns whether the given account is an admin.
function isAdmin(address signer) external view returns (bool);
/// @notice Returns whether the given account is an active signer on the account.
function isActiveSigner(address signer) external view returns (bool);
/// @notice Returns the restrictions under which a signer can use the smart wallet.
function getPermissionsForSigner(address signer) external view returns (SignerPermissions memory permissions);
/// @notice Returns all active and inactive signers of the account.
function getAllSigners() external view returns (SignerPermissions[] memory signers);
/// @notice Returns all signers with active permissions to use the account.
function getAllActiveSigners() external view returns (SignerPermissions[] memory signers);
/// @notice Returns all admins of the account.
function getAllAdmins() external view returns (address[] memory admins);
/// @dev Verifies that a request is signed by an authorized account.
function verifySignerPermissionRequest(
SignerPermissionRequest calldata req,
bytes calldata signature
) external view returns (bool success, address signer);
/*///////////////////////////////////////////////////////////////
External functions
//////////////////////////////////////////////////////////////*/
/// @notice Sets the permissions for a given signer.
function setPermissionsForSigner(SignerPermissionRequest calldata req, bytes calldata signature) external;
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @author thirdweb
/**
* Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
* for you contract.
*
* Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
*/
interface IContractMetadata {
/// @dev Returns the metadata URI of the contract.
function contractURI() external view returns (string memory);
/**
* @dev Sets contract URI for the storefront-level metadata of the contract.
* Only module admin can call this function.
*/
function setContractURI(string calldata _uri) external;
/// @dev Emitted when the contract URI is updated.
event ContractURIUpdated(string prevURI, string newURI);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @author thirdweb
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* _Available since v4.1._
*/
interface IMulticall {
/**
* @dev Receives and executes a batch of function calls on this contract.
*/
function multicall(bytes[] calldata data) external returns (bytes[] memory results);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @author thirdweb
import "../interface/IAccountPermissions.sol";
import "../../external-deps/openzeppelin/utils/cryptography/EIP712.sol";
import "../../external-deps/openzeppelin/utils/structs/EnumerableSet.sol";
library AccountPermissionsStorage {
/// @custom:storage-location erc7201:account.permissions.storage
/// @dev keccak256(abi.encode(uint256(keccak256("account.permissions.storage")) - 1)) & ~bytes32(uint256(0xff))
bytes32 public constant ACCOUNT_PERMISSIONS_STORAGE_POSITION =
0x3181e78fc1b109bc611fd2406150bf06e33faa75f71cba12c3e1fd670f2def00;
struct Data {
/// @dev The set of all admins of the wallet.
EnumerableSet.AddressSet allAdmins;
/// @dev The set of all signers with permission to use the account.
EnumerableSet.AddressSet allSigners;
/// @dev Map from address => whether the address is an admin.
mapping(address => bool) isAdmin;
/// @dev Map from signer address => active restrictions for that signer.
mapping(address => IAccountPermissions.SignerPermissionsStatic) signerPermissions;
/// @dev Map from signer address => approved target the signer can call using the account contract.
mapping(address => EnumerableSet.AddressSet) approvedTargets;
/// @dev Mapping from a signed request UID => whether the request is processed.
mapping(bytes32 => bool) executed;
}
function data() internal pure returns (Data storage data_) {
bytes32 position = ACCOUNT_PERMISSIONS_STORAGE_POSITION;
assembly {
data_.slot := position
}
}
}
abstract contract AccountPermissions is IAccountPermissions, EIP712 {
using ECDSA for bytes32;
using EnumerableSet for EnumerableSet.AddressSet;
bytes32 private constant TYPEHASH =
keccak256(
"SignerPermissionRequest(address signer,uint8 isAdmin,address[] approvedTargets,uint256 nativeTokenLimitPerTransaction,uint128 permissionStartTimestamp,uint128 permissionEndTimestamp,uint128 reqValidityStartTimestamp,uint128 reqValidityEndTimestamp,bytes32 uid)"
);
function _onlyAdmin() internal virtual {
require(isAdmin(msg.sender), "!admin");
}
/*///////////////////////////////////////////////////////////////
External functions
//////////////////////////////////////////////////////////////*/
/// @notice Sets the permissions for a given signer.
function setPermissionsForSigner(SignerPermissionRequest calldata _req, bytes calldata _signature) external {
address targetSigner = _req.signer;
require(
_req.reqValidityStartTimestamp <= block.timestamp && block.timestamp < _req.reqValidityEndTimestamp,
"!period"
);
(bool success, address signer) = verifySignerPermissionRequest(_req, _signature);
require(success, "!sig");
_accountPermissionsStorage().executed[_req.uid] = true;
//isAdmin > 0, set admin or remove admin
if (_req.isAdmin > 0) {
//isAdmin = 1, set admin
//isAdmin > 1, remove admin
bool _isAdmin = _req.isAdmin == 1;
_setAdmin(targetSigner, _isAdmin);
return;
}
require(!isAdmin(targetSigner), "admin");
_accountPermissionsStorage().allSigners.add(targetSigner);
_accountPermissionsStorage().signerPermissions[targetSigner] = SignerPermissionsStatic(
_req.nativeTokenLimitPerTransaction,
_req.permissionStartTimestamp,
_req.permissionEndTimestamp
);
address[] memory currentTargets = _accountPermissionsStorage().approvedTargets[targetSigner].values();
uint256 len = currentTargets.length;
for (uint256 i = 0; i < len; i += 1) {
_accountPermissionsStorage().approvedTargets[targetSigner].remove(currentTargets[i]);
}
len = _req.approvedTargets.length;
for (uint256 i = 0; i < len; i += 1) {
_accountPermissionsStorage().approvedTargets[targetSigner].add(_req.approvedTargets[i]);
}
_afterSignerPermissionsUpdate(_req);
emit SignerPermissionsUpdated(signer, targetSigner, _req);
}
/*///////////////////////////////////////////////////////////////
View functions
//////////////////////////////////////////////////////////////*/
/// @notice Returns whether the given account is an admin.
function isAdmin(address _account) public view virtual returns (bool) {
return _accountPermissionsStorage().isAdmin[_account];
}
/// @notice Returns whether the given account is an active signer on the account.
function isActiveSigner(address signer) public view returns (bool) {
SignerPermissionsStatic memory permissions = _accountPermissionsStorage().signerPermissions[signer];
return
permissions.startTimestamp <= block.timestamp &&
block.timestamp < permissions.endTimestamp &&
_accountPermissionsStorage().approvedTargets[signer].length() > 0;
}
/// @notice Returns the restrictions under which a signer can use the smart wallet.
function getPermissionsForSigner(address signer) external view returns (SignerPermissions memory) {
SignerPermissionsStatic memory permissions = _accountPermissionsStorage().signerPermissions[signer];
return
SignerPermissions(
signer,
_accountPermissionsStorage().approvedTargets[signer].values(),
permissions.nativeTokenLimitPerTransaction,
permissions.startTimestamp,
permissions.endTimestamp
);
}
/// @dev Verifies that a request is signed by an authorized account.
function verifySignerPermissionRequest(
SignerPermissionRequest calldata req,
bytes calldata signature
) public view virtual returns (bool success, address signer) {
signer = _recoverAddress(_encodeRequest(req), signature);
success = !_accountPermissionsStorage().executed[req.uid] && isAdmin(signer);
}
/// @notice Returns all active and inactive signers of the account.
function getAllSigners() external view returns (SignerPermissions[] memory signers) {
address[] memory allSigners = _accountPermissionsStorage().allSigners.values();
uint256 len = allSigners.length;
signers = new SignerPermissions[](len);
for (uint256 i = 0; i < len; i += 1) {
address signer = allSigners[i];
SignerPermissionsStatic memory permissions = _accountPermissionsStorage().signerPermissions[signer];
signers[i] = SignerPermissions(
signer,
_accountPermissionsStorage().approvedTargets[signer].values(),
permissions.nativeTokenLimitPerTransaction,
permissions.startTimestamp,
permissions.endTimestamp
);
}
}
/// @notice Returns all signers with active permissions to use the account.
function getAllActiveSigners() external view returns (SignerPermissions[] memory signers) {
address[] memory allSigners = _accountPermissionsStorage().allSigners.values();
uint256 len = allSigners.length;
uint256 numOfActiveSigners = 0;
for (uint256 i = 0; i < len; i += 1) {
if (isActiveSigner(allSigners[i])) {
numOfActiveSigners++;
} else {
allSigners[i] = address(0);
}
}
signers = new SignerPermissions[](numOfActiveSigners);
uint256 index = 0;
for (uint256 i = 0; i < len; i += 1) {
if (allSigners[i] != address(0)) {
address signer = allSigners[i];
SignerPermissionsStatic memory permissions = _accountPermissionsStorage().signerPermissions[signer];
signers[index++] = SignerPermissions(
signer,
_accountPermissionsStorage().approvedTargets[signer].values(),
permissions.nativeTokenLimitPerTransaction,
permissions.startTimestamp,
permissions.endTimestamp
);
}
}
}
/// @notice Returns all admins of the account.
function getAllAdmins() external view returns (address[] memory) {
return _accountPermissionsStorage().allAdmins.values();
}
/*///////////////////////////////////////////////////////////////
Internal functions
//////////////////////////////////////////////////////////////*/
/// @notice Runs after every `changeRole` run.
function _afterSignerPermissionsUpdate(SignerPermissionRequest calldata _req) internal virtual;
/// @notice Makes the given account an admin.
function _setAdmin(address _account, bool _isAdmin) internal virtual {
_accountPermissionsStorage().isAdmin[_account] = _isAdmin;
if (_isAdmin) {
_accountPermissionsStorage().allAdmins.add(_account);
} else {
_accountPermissionsStorage().allAdmins.remove(_account);
}
emit AdminUpdated(_account, _isAdmin);
}
/// @dev Returns the address of the signer of the request.
function _recoverAddress(bytes memory _encoded, bytes calldata _signature) internal view virtual returns (address) {
return _hashTypedDataV4(keccak256(_encoded)).recover(_signature);
}
/// @dev Encodes a request for recovery of the signer in `recoverAddress`.
function _encodeRequest(SignerPermissionRequest calldata _req) internal pure virtual returns (bytes memory) {
return
abi.encode(
TYPEHASH,
_req.signer,
_req.isAdmin,
keccak256(abi.encodePacked(_req.approvedTargets)),
_req.nativeTokenLimitPerTransaction,
_req.permissionStartTimestamp,
_req.permissionEndTimestamp,
_req.reqValidityStartTimestamp,
_req.reqValidityEndTimestamp,
_req.uid
);
}
/// @dev Returns the AccountPermissions storage.
function _accountPermissionsStorage() internal pure returns (AccountPermissionsStorage.Data storage data) {
data = AccountPermissionsStorage.data();
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @author thirdweb
import "../interface/IContractMetadata.sol";
/**
* @author thirdweb.com
*
* @title Contract Metadata
* @notice Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
* for you contract.
* Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
*/
library ContractMetadataStorage {
/// @custom:storage-location erc7201:contract.metadata.storage
/// @dev keccak256(abi.encode(uint256(keccak256("contract.metadata.storage")) - 1)) & ~bytes32(uint256(0xff))
bytes32 public constant CONTRACT_METADATA_STORAGE_POSITION =
0x4bc804ba64359c0e35e5ed5d90ee596ecaa49a3a930ddcb1470ea0dd625da900;
struct Data {
/// @notice Returns the contract metadata URI.
string contractURI;
}
function data() internal pure returns (Data storage data_) {
bytes32 position = CONTRACT_METADATA_STORAGE_POSITION;
assembly {
data_.slot := position
}
}
}
abstract contract ContractMetadata is IContractMetadata {
/**
* @notice Lets a contract admin set the URI for contract-level metadata.
* @dev Caller should be authorized to setup contractURI, e.g. contract admin.
* See {_canSetContractURI}.
* Emits {ContractURIUpdated Event}.
*
* @param _uri keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
*/
function setContractURI(string memory _uri) external override {
if (!_canSetContractURI()) {
revert("Not authorized");
}
_setupContractURI(_uri);
}
/// @dev Lets a contract admin set the URI for contract-level metadata.
function _setupContractURI(string memory _uri) internal {
string memory prevURI = _contractMetadataStorage().contractURI;
_contractMetadataStorage().contractURI = _uri;
emit ContractURIUpdated(prevURI, _uri);
}
/// @notice Returns the contract metadata URI.
function contractURI() public view virtual override returns (string memory) {
return _contractMetadataStorage().contractURI;
}
/// @dev Returns the AccountPermissions storage.
function _contractMetadataStorage() internal pure returns (ContractMetadataStorage.Data storage data) {
data = ContractMetadataStorage.data();
}
/// @dev Returns whether contract metadata can be set in the given execution context.
function _canSetContractURI() internal view virtual returns (bool);
}// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.0;
import "../../lib/Address.sol";
library InitStorage {
/// @custom:storage-location erc7201:init.storage
/// @dev keccak256(abi.encode(uint256(keccak256("init.storage")) - 1)) & ~bytes32(uint256(0xff))
bytes32 constant INIT_STORAGE_POSITION = 0x322cf19c484104d3b1a9c2982ebae869ede3fa5f6c4703ca41b9a48c76ee0300;
/// @dev Layout of the entrypoint contract's storage.
struct Data {
uint8 initialized;
bool initializing;
}
/// @dev Returns the entrypoint contract's data at the relevant storage location.
function data() internal pure returns (Data storage data_) {
bytes32 position = INIT_STORAGE_POSITION;
assembly {
data_.slot := position
}
}
}
abstract contract Initializable {
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
*/
modifier initializer() {
uint8 _initialized = _initStorage().initialized;
bool _initializing = _initStorage().initializing;
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initStorage().initialized = 1;
if (isTopLevelCall) {
_initStorage().initializing = true;
}
_;
if (isTopLevelCall) {
_initStorage().initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
* initialization step. This is essential to configure modules that are added through upgrades and that require
* initialization.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*/
modifier reinitializer(uint8 version) {
uint8 _initialized = _initStorage().initialized;
bool _initializing = _initStorage().initializing;
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initStorage().initialized = version;
_initStorage().initializing = true;
_;
_initStorage().initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initStorage().initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*/
function _disableInitializers() internal virtual {
uint8 _initialized = _initStorage().initialized;
bool _initializing = _initStorage().initializing;
require(!_initializing, "Initializable: contract is initializing");
if (_initialized < type(uint8).max) {
_initStorage().initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/// @dev Returns the InitStorage storage.
function _initStorage() internal pure returns (InitStorage.Data storage data) {
data = InitStorage.data();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (proxy/Clones.sol)
pragma solidity ^0.8.0;
/**
* @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
* deploying minimal proxy contracts, also known as "clones".
*
* > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
* > a minimal bytecode implementation that delegates all calls to a known, fixed address.
*
* The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
* (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
* deterministic method.
*
* _Available since v3.4._
*/
library Clones {
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create opcode, which should never revert.
*/
function clone(address implementation) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create(0, 0x09, 0x37)
}
require(instance != address(0), "ERC1167: create failed");
}
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy
* the clone. Using the same `implementation` and `salt` multiple time will revert, since
* the clones cannot be deployed twice at the same address.
*/
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create2(0, 0x09, 0x37, salt)
}
require(instance != address(0), "ERC1167: create2 failed");
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(add(ptr, 0x38), deployer)
mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
mstore(add(ptr, 0x14), implementation)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
mstore(add(ptr, 0x58), salt)
mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
predicted := keccak256(add(ptr, 0x43), 0x55)
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt
) internal view returns (address predicted) {
return predictDeterministicAddress(implementation, salt, address(this));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)
pragma solidity ^0.8.0;
import "../../../../eip/interface/IERC165.sol";
/**
* @dev _Available since v3.1._
*/
interface IERC1155Receiver is IERC165 {
/**
* @dev Handles the receipt of a single ERC1155 token type. This function is
* called at the end of a `safeTransferFrom` after the balance has been updated.
*
* NOTE: To accept the transfer, this must return
* `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
* (i.e. 0xf23a6e61, or its own function selector).
*
* @param operator The address which initiated the transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param id The ID of the token being transferred
* @param value The amount of tokens being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
/**
* @dev Handles the receipt of a multiple ERC1155 token types. This function
* is called at the end of a `safeBatchTransferFrom` after the balances have
* been updated.
*
* NOTE: To accept the transfer(s), this must return
* `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
* (i.e. 0xbc197c81, or its own function selector).
*
* @param operator The address which initiated the batch transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param ids An array containing ids of each token being transferred (order and length must match values array)
* @param values An array containing amounts of each token being transferred (order and length must match ids array)
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/utils/ERC1155Holder.sol)
pragma solidity ^0.8.0;
import "./ERC1155Receiver.sol";
/**
* Simple implementation of `ERC1155Receiver` that will allow a contract to hold ERC1155 tokens.
*
* IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be
* stuck.
*
* @dev _Available since v3.1._
*/
contract ERC1155Holder is ERC1155Receiver {
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] memory,
uint256[] memory,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155BatchReceived.selector;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol)
pragma solidity ^0.8.0;
import "../IERC1155Receiver.sol";
import "../../../../../eip/ERC165.sol";
/**
* @dev _Available since v3.1._
*/
abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.0;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
*
* The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/utils/ERC721Holder.sol)
pragma solidity ^0.8.0;
import "../IERC721Receiver.sol";
/**
* @dev Implementation of the {IERC721Receiver} interface.
*
* Accepts all token transfers.
* Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
*/
contract ERC721Holder is IERC721Receiver {
/**
* @dev See {IERC721Receiver-onERC721Received}.
*
* Always returns `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) {
return this.onERC721Received.selector;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../../../../lib/Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32")
mstore(0x1c, hash)
message := keccak256(0x00, 0x3c)
}
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, "\x19\x01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
data := keccak256(ptr, 0x42)
}
}
/**
* @dev Returns an Ethereum Signed Data with intended validator, created from a
* `validator` and `data` according to the version 0 of EIP-191.
*
* See {recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x00", validator, data));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol)
pragma solidity ^0.8.0;
import "./ECDSA.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
* thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
* they need in their contracts using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* _Available since v3.4._
*/
abstract contract EIP712 {
/* solhint-disable var-name-mixedcase */
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
uint256 private immutable _CACHED_CHAIN_ID;
address private immutable _CACHED_THIS;
bytes32 private immutable _HASHED_NAME;
bytes32 private immutable _HASHED_VERSION;
bytes32 private immutable _TYPE_HASH;
/* solhint-enable var-name-mixedcase */
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) {
bytes32 hashedName = keccak256(bytes(name));
bytes32 hashedVersion = keccak256(bytes(version));
bytes32 typeHash = keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
_HASHED_NAME = hashedName;
_HASHED_VERSION = hashedVersion;
_CACHED_CHAIN_ID = block.chainid;
_CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
_CACHED_THIS = address(this);
_TYPE_HASH = typeHash;
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
return _CACHED_DOMAIN_SEPARATOR;
} else {
return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
}
}
function _buildDomainSeparator(
bytes32 typeHash,
bytes32 nameHash,
bytes32 versionHash
) private view returns (bytes32) {
return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
return _values(set._inner);
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.1;
/// @author thirdweb, OpenZeppelin Contracts (v4.9.0)
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @author thirdweb
/// Credits: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol
library BytesLib {
function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
address tempAddress;
assembly {
tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
}
return tempAddress;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @author thirdweb
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/// @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.
}
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
import "../utils/UserOperation.sol";
interface IAccount {
/**
* Validate user's signature and nonce
* the entryPoint will make the call to the recipient only if this validation call returns successfully.
* signature failure should be reported by returning SIG_VALIDATION_FAILED (1).
* This allows making a "simulation call" without a valid signature
* Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure.
*
* @dev Must validate caller is the entryPoint.
* Must validate the signature and nonce
* @param userOp the operation that is about to be executed.
* @param userOpHash hash of the user's request data. can be used as the basis for signature.
* @param missingAccountFunds missing funds on the account's deposit in the entrypoint.
* This is the minimum amount to transfer to the sender(entryPoint) to be able to make the call.
* The excess is left as a deposit in the entrypoint, for future calls.
* can be withdrawn anytime using "entryPoint.withdrawTo()"
* In case there is a paymaster in the request (or the current deposit is high enough), this value will be zero.
* @return validationData packaged ValidationData structure. use `_packValidationData` and `_unpackValidationData` to encode and decode
* <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure,
* otherwise, an address of an "authorizer" contract.
* <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite"
* <6-byte> validAfter - first timestamp this operation is valid
* If an account doesn't use time-range, it is enough to return SIG_VALIDATION_FAILED value (1) for signature failure.
* Note that the validation code cannot use block.timestamp (or block.number) directly.
*/
function validateUserOp(
UserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external returns (uint256 validationData);
}// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.12;
import "./IAccount.sol";
import "../../../extension/interface/IAccountPermissions.sol";
import "../../../extension/interface/IMulticall.sol";
interface IAccountCore is IAccount, IAccountPermissions, IMulticall {
/// @dev Returns the address of the factory from which the account was created.
function factory() external view returns (address);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
import "./IAccountFactoryCore.sol";
interface IAccountFactory is IAccountFactoryCore {
/*///////////////////////////////////////////////////////////////
Callback Functions
//////////////////////////////////////////////////////////////*/
/// @notice Callback function for an Account to register its signers.
function onSignerAdded(address signer, bytes32 salt) external;
/// @notice Callback function for an Account to un-register its signers.
function onSignerRemoved(address signer, bytes32 salt) external;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
interface IAccountFactoryCore {
/*///////////////////////////////////////////////////////////////
Events
//////////////////////////////////////////////////////////////*/
/// @notice Emitted when a new Account is created.
event AccountCreated(address indexed account, address indexed accountAdmin);
/// @notice Emitted when a new signer is added to an Account.
event SignerAdded(address indexed account, address indexed signer);
/// @notice Emitted when a new signer is added to an Account.
event SignerRemoved(address indexed account, address indexed signer);
/*///////////////////////////////////////////////////////////////
Extension Functions
//////////////////////////////////////////////////////////////*/
/// @notice Deploys a new Account for admin.
function createAccount(address admin, bytes calldata _data) external returns (address account);
/*///////////////////////////////////////////////////////////////
View Functions
//////////////////////////////////////////////////////////////*/
/// @notice Returns the address of the Account implementation.
function accountImplementation() external view returns (address);
/// @notice Returns all accounts created on the factory.
function getAllAccounts() external view returns (address[] memory);
/// @notice Returns the address of an Account that would be deployed with the given admin signer.
function getAddress(address adminSigner, bytes calldata data) external view returns (address);
/// @notice Returns all accounts on which a signer has (active or inactive) permissions.
function getAccountsOfSigner(address signer) external view returns (address[] memory accounts);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
import "../utils/UserOperation.sol";
/**
* Aggregated Signatures validator.
*/
interface IAggregator {
/**
* validate aggregated signature.
* revert if the aggregated signature does not match the given list of operations.
*/
function validateSignatures(UserOperation[] calldata userOps, bytes calldata signature) external view;
/**
* validate signature of a single userOp
* This method is should be called by bundler after EntryPoint.simulateValidation() returns (reverts) with ValidationResultWithAggregation
* First it validates the signature over the userOp. Then it returns data to be used when creating the handleOps.
* @param userOp the userOperation received from the user.
* @return sigForUserOp the value to put into the signature field of the userOp when calling handleOps.
* (usually empty, unless account and aggregator support some kind of "multisig"
*/
function validateUserOpSignature(UserOperation calldata userOp) external view returns (bytes memory sigForUserOp);
/**
* aggregate multiple signatures into a single value.
* This method is called off-chain to calculate the signature to pass with handleOps()
* bundler MAY use optimized custom code perform this aggregation
* @param userOps array of UserOperations to collect the signatures from.
* @return aggregatedSignature the aggregated signature
*/
function aggregateSignatures(
UserOperation[] calldata userOps
) external view returns (bytes memory aggregatedSignature);
}/**
** Account-Abstraction (EIP-4337) singleton EntryPoint implementation.
** Only one instance required on each chain.
**/
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-inline-assembly */
/* solhint-disable reason-string */
import "../utils/UserOperation.sol";
import "./IStakeManager.sol";
import "./IAggregator.sol";
import "./INonceManager.sol";
interface IEntryPoint is IStakeManager, INonceManager {
/***
* An event emitted after each successful request
* @param userOpHash - unique identifier for the request (hash its entire content, except signature).
* @param sender - the account that generates this request.
* @param paymaster - if non-null, the paymaster that pays for this request.
* @param nonce - the nonce value from the request.
* @param success - true if the sender transaction succeeded, false if reverted.
* @param actualGasCost - actual amount paid (by account or paymaster) for this UserOperation.
* @param actualGasUsed - total gas used by this UserOperation (including preVerification, creation, validation and execution).
*/
event UserOperationEvent(
bytes32 indexed userOpHash,
address indexed sender,
address indexed paymaster,
uint256 nonce,
bool success,
uint256 actualGasCost,
uint256 actualGasUsed
);
/**
* account "sender" was deployed.
* @param userOpHash the userOp that deployed this account. UserOperationEvent will follow.
* @param sender the account that is deployed
* @param factory the factory used to deploy this account (in the initCode)
* @param paymaster the paymaster used by this UserOp
*/
event AccountDeployed(bytes32 indexed userOpHash, address indexed sender, address factory, address paymaster);
/**
* An event emitted if the UserOperation "callData" reverted with non-zero length
* @param userOpHash the request unique identifier.
* @param sender the sender of this request
* @param nonce the nonce used in the request
* @param revertReason - the return bytes from the (reverted) call to "callData".
*/
event UserOperationRevertReason(
bytes32 indexed userOpHash,
address indexed sender,
uint256 nonce,
bytes revertReason
);
/**
* an event emitted by handleOps(), before starting the execution loop.
* any event emitted before this event, is part of the validation.
*/
event BeforeExecution();
/**
* signature aggregator used by the following UserOperationEvents within this bundle.
*/
event SignatureAggregatorChanged(address indexed aggregator);
/**
* a custom revert error of handleOps, to identify the offending op.
* NOTE: if simulateValidation passes successfully, there should be no reason for handleOps to fail on it.
* @param opIndex - index into the array of ops to the failed one (in simulateValidation, this is always zero)
* @param reason - revert reason
* The string starts with a unique code "AAmn", where "m" is "1" for factory, "2" for account and "3" for paymaster issues,
* so a failure can be attributed to the correct entity.
* Should be caught in off-chain handleOps simulation and not happen on-chain.
* Useful for mitigating DoS attempts against batchers or for troubleshooting of factory/account/paymaster reverts.
*/
error FailedOp(uint256 opIndex, string reason);
/**
* error case when a signature aggregator fails to verify the aggregated signature it had created.
*/
error SignatureValidationFailed(address aggregator);
/**
* Successful result from simulateValidation.
* @param returnInfo gas and time-range returned values
* @param senderInfo stake information about the sender
* @param factoryInfo stake information about the factory (if any)
* @param paymasterInfo stake information about the paymaster (if any)
*/
error ValidationResult(ReturnInfo returnInfo, StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo);
/**
* Successful result from simulateValidation, if the account returns a signature aggregator
* @param returnInfo gas and time-range returned values
* @param senderInfo stake information about the sender
* @param factoryInfo stake information about the factory (if any)
* @param paymasterInfo stake information about the paymaster (if any)
* @param aggregatorInfo signature aggregation info (if the account requires signature aggregator)
* bundler MUST use it to verify the signature, or reject the UserOperation
*/
error ValidationResultWithAggregation(
ReturnInfo returnInfo,
StakeInfo senderInfo,
StakeInfo factoryInfo,
StakeInfo paymasterInfo,
AggregatorStakeInfo aggregatorInfo
);
/**
* return value of getSenderAddress
*/
error SenderAddressResult(address sender);
/**
* return value of simulateHandleOp
*/
error ExecutionResult(
uint256 preOpGas,
uint256 paid,
uint48 validAfter,
uint48 validUntil,
bool targetSuccess,
bytes targetResult
);
//UserOps handled, per aggregator
struct UserOpsPerAggregator {
UserOperation[] userOps;
// aggregator address
IAggregator aggregator;
// aggregated signature
bytes signature;
}
/**
* Execute a batch of UserOperation.
* no signature aggregator is used.
* if any account requires an aggregator (that is, it returned an aggregator when
* performing simulateValidation), then handleAggregatedOps() must be used instead.
* @param ops the operations to execute
* @param beneficiary the address to receive the fees
*/
function handleOps(UserOperation[] calldata ops, address payable beneficiary) external;
/**
* Execute a batch of UserOperation with Aggregators
* @param opsPerAggregator the operations to execute, grouped by aggregator (or address(0) for no-aggregator accounts)
* @param beneficiary the address to receive the fees
*/
function handleAggregatedOps(
UserOpsPerAggregator[] calldata opsPerAggregator,
address payable beneficiary
) external;
/**
* generate a request Id - unique identifier for this request.
* the request ID is a hash over the content of the userOp (except the signature), the entrypoint and the chainid.
*/
function getUserOpHash(UserOperation calldata userOp) external view returns (bytes32);
/**
* Simulate a call to account.validateUserOp and paymaster.validatePaymasterUserOp.
* @dev this method always revert. Successful result is ValidationResult error. other errors are failures.
* @dev The node must also verify it doesn't use banned opcodes, and that it doesn't reference storage outside the account's data.
* @param userOp the user operation to validate.
*/
function simulateValidation(UserOperation calldata userOp) external;
/**
* gas and return values during simulation
* @param preOpGas the gas used for validation (including preValidationGas)
* @param prefund the required prefund for this operation
* @param sigFailed validateUserOp's (or paymaster's) signature check failed
* @param validAfter - first timestamp this UserOp is valid (merging account and paymaster time-range)
* @param validUntil - last timestamp this UserOp is valid (merging account and paymaster time-range)
* @param paymasterContext returned by validatePaymasterUserOp (to be passed into postOp)
*/
struct ReturnInfo {
uint256 preOpGas;
uint256 prefund;
bool sigFailed;
uint48 validAfter;
uint48 validUntil;
bytes paymasterContext;
}
/**
* returned aggregated signature info.
* the aggregator returned by the account, and its current stake.
*/
struct AggregatorStakeInfo {
address aggregator;
StakeInfo stakeInfo;
}
/**
* Get counterfactual sender address.
* Calculate the sender contract address that will be generated by the initCode and salt in the UserOperation.
* this method always revert, and returns the address in SenderAddressResult error
* @param initCode the constructor code to be passed into the UserOperation.
*/
function getSenderAddress(bytes memory initCode) external;
/**
* simulate full execution of a UserOperation (including both validation and target execution)
* this method will always revert with "ExecutionResult".
* it performs full validation of the UserOperation, but ignores signature error.
* an optional target address is called after the userop succeeds, and its value is returned
* (before the entire call is reverted)
* Note that in order to collect the success/failure of the target call, it must be executed
* with trace enabled to track the emitted events.
* @param op the UserOperation to simulate
* @param target if nonzero, a target address to call after userop simulation. If called, the targetSuccess and targetResult
* are set to the return from that call.
* @param targetCallData callData to pass to target address
*/
function simulateHandleOp(UserOperation calldata op, address target, bytes calldata targetCallData) external;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
interface INonceManager {
/**
* Return the next nonce for this sender.
* Within a given key, the nonce values are sequenced (starting with zero, and incremented by one on each userop)
* But UserOp with different keys can come with arbitrary order.
*
* @param sender the account address
* @param key the high 192 bit of the nonce
* @return nonce a full nonce to pass for next UserOp with this sender.
*/
function getNonce(address sender, uint192 key) external view returns (uint256 nonce);
/**
* Manually increment the nonce of the sender.
* This method is exposed just for completeness..
* Account does NOT need to call it, neither during validation, nor elsewhere,
* as the EntryPoint will update the nonce regardless.
* Possible use-case is call it with various keys to "initialize" their nonces to one, so that future
* UserOperations will not pay extra for the first transaction with a given key.
*/
function incrementNonce(uint192 key) external;
}// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.12;
/**
* manage deposits and stakes.
* deposit is just a balance used to pay for UserOperations (either by a paymaster or an account)
* stake is value locked for at least "unstakeDelay" by the staked entity.
*/
interface IStakeManager {
event Deposited(address indexed account, uint256 totalDeposit);
event Withdrawn(address indexed account, address withdrawAddress, uint256 amount);
/// Emitted when stake or unstake delay are modified
event StakeLocked(address indexed account, uint256 totalStaked, uint256 unstakeDelaySec);
/// Emitted once a stake is scheduled for withdrawal
event StakeUnlocked(address indexed account, uint256 withdrawTime);
event StakeWithdrawn(address indexed account, address withdrawAddress, uint256 amount);
/**
* @param deposit the entity's deposit
* @param staked true if this entity is staked.
* @param stake actual amount of ether staked for this entity.
* @param unstakeDelaySec minimum delay to withdraw the stake.
* @param withdrawTime - first block timestamp where 'withdrawStake' will be callable, or zero if already locked
* @dev sizes were chosen so that (deposit,staked, stake) fit into one cell (used during handleOps)
* and the rest fit into a 2nd cell.
* 112 bit allows for 10^15 eth
* 48 bit for full timestamp
* 32 bit allows 150 years for unstake delay
*/
struct DepositInfo {
uint112 deposit;
bool staked;
uint112 stake;
uint32 unstakeDelaySec;
uint48 withdrawTime;
}
//API struct used by getStakeInfo and simulateValidation
struct StakeInfo {
uint256 stake;
uint256 unstakeDelaySec;
}
/// @return info - full deposit information of given account
function getDepositInfo(address account) external view returns (DepositInfo memory info);
/// @return the deposit (for gas payment) of the account
function balanceOf(address account) external view returns (uint256);
/**
* add to the deposit of the given account
*/
function depositTo(address account) external payable;
/**
* add to the account's stake - amount and delay
* any pending unstake is first cancelled.
* @param _unstakeDelaySec the new lock duration before the deposit can be withdrawn.
*/
function addStake(uint32 _unstakeDelaySec) external payable;
/**
* attempt to unlock the stake.
* the value can be withdrawn (using withdrawStake) after the unstake delay.
*/
function unlockStake() external;
/**
* withdraw from the (unlocked) stake.
* must first call unlockStake and wait for the unstakeDelay to pass
* @param withdrawAddress the address to send withdrawn value.
*/
function withdrawStake(address payable withdrawAddress) external;
/**
* withdraw from the deposit.
* @param withdrawAddress the address to send withdrawn value.
* @param withdrawAmount the amount to withdraw.
*/
function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external;
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.11;
/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-inline-assembly */
/* solhint-disable reason-string */
// Base
import "./../utils/BaseAccount.sol";
// Fixed Extensions
import "../../../extension/Multicall.sol";
import "../../../extension/upgradeable/Initializable.sol";
import "../../../extension/upgradeable/AccountPermissions.sol";
// Utils
import "./Helpers.sol";
import "./AccountCoreStorage.sol";
import "./BaseAccountFactory.sol";
import { AccountExtension } from "./AccountExtension.sol";
import "../../../external-deps/openzeppelin/utils/cryptography/ECDSA.sol";
import "../interface/IAccountCore.sol";
// $$\ $$\ $$\ $$\ $$\
// $$ | $$ | \__| $$ | $$ |
// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\
// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\
// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ |
// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ |
// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ |
// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/
contract AccountCore is IAccountCore, Initializable, Multicall, BaseAccount, AccountPermissions {
using ECDSA for bytes32;
using EnumerableSet for EnumerableSet.AddressSet;
/*///////////////////////////////////////////////////////////////
State
//////////////////////////////////////////////////////////////*/
/// @notice EIP 4337 factory for this contract.
address public immutable factory;
/// @notice EIP 4337 Entrypoint contract.
IEntryPoint private immutable entrypointContract;
/*///////////////////////////////////////////////////////////////
Constructor, Initializer, Modifiers
//////////////////////////////////////////////////////////////*/
constructor(IEntryPoint _entrypoint, address _factory) EIP712("Account", "1") {
_disableInitializers();
factory = _factory;
entrypointContract = _entrypoint;
}
/// @notice Initializes the smart contract wallet.
function initialize(address _defaultAdmin, bytes calldata _data) public virtual initializer {
// This is passed as data in the `_registerOnFactory()` call in `AccountExtension` / `Account`.
AccountCoreStorage.data().creationSalt = _generateSalt(_defaultAdmin, _data);
_setAdmin(_defaultAdmin, true);
}
/*///////////////////////////////////////////////////////////////
View functions
//////////////////////////////////////////////////////////////*/
/// @notice Returns the EIP 4337 entrypoint contract.
function entryPoint() public view virtual override returns (IEntryPoint) {
address entrypointOverride = AccountCoreStorage.data().entrypointOverride;
if (address(entrypointOverride) != address(0)) {
return IEntryPoint(entrypointOverride);
}
return entrypointContract;
}
/**
@notice Returns whether a signer is authorized to perform transactions using the account.
Validity of the signature is based upon signer permission start/end timestamps, txn target, and txn value.
Account admins will always return true, and signers with address(0) as the only approved target will skip target checks.
@param _signer The signer to check.
@param _userOp The user operation to check.
@return Whether the signer is authorized to perform the transaction.
*/
/* solhint-disable*/
function isValidSigner(address _signer, UserOperation calldata _userOp) public view virtual returns (bool) {
// First, check if the signer is an admin.
if (_accountPermissionsStorage().isAdmin[_signer]) {
return true;
}
SignerPermissionsStatic memory permissions = _accountPermissionsStorage().signerPermissions[_signer];
EnumerableSet.AddressSet storage approvedTargets = _accountPermissionsStorage().approvedTargets[_signer];
// If not an admin, check if the signer is active.
if (
permissions.startTimestamp > block.timestamp ||
block.timestamp >= permissions.endTimestamp ||
approvedTargets.length() == 0
) {
// Account: no active permissions.
return false;
}
// Extract the function signature from the userOp calldata and check whether the signer is attempting to call `execute` or `executeBatch`.
bytes4 sig = getFunctionSignature(_userOp.callData);
// if address(0) is the only approved target, set isWildCard to true (wildcard approved).
bool isWildCard = approvedTargets.length() == 1 && approvedTargets.at(0) == address(0);
// checking target and value for `execute`
if (sig == AccountExtension.execute.selector) {
// Extract the `target` and `value` arguments from the calldata for `execute`.
(address target, uint256 value) = decodeExecuteCalldata(_userOp.callData);
// if wildcard target is not approved, check that the target is in the approvedTargets set.
if (!isWildCard) {
// Check if the target is approved.
if (!approvedTargets.contains(target)) {
// Account: target not approved.
return false;
}
}
// Check if the value is within the allowed range.
if (permissions.nativeTokenLimitPerTransaction < value) {
// Account: value too high OR Account: target not approved.
return false;
}
}
// checking target and value for `executeBatch`
else if (sig == AccountExtension.executeBatch.selector) {
// Extract the `target` and `value` array arguments from the calldata for `executeBatch`.
(address[] memory targets, uint256[] memory values, ) = decodeExecuteBatchCalldata(_userOp.callData);
// if wildcard target is not approved, check that the targets are in the approvedTargets set.
if (!isWildCard) {
for (uint256 i = 0; i < targets.length; i++) {
if (!approvedTargets.contains(targets[i])) {
// If any target is not approved, break the loop.
return false;
}
}
}
// For each target+value pair, check if the value is within the allowed range.
for (uint256 i = 0; i < targets.length; i++) {
if (permissions.nativeTokenLimitPerTransaction < values[i]) {
// Account: value too high OR Account: target not approved.
return false;
}
}
} else {
// Account: calling invalid fn.
return false;
}
return true;
}
/* solhint-enable */
/*///////////////////////////////////////////////////////////////
External functions
//////////////////////////////////////////////////////////////*/
/// @notice Overrides the Entrypoint contract being used.
function setEntrypointOverride(IEntryPoint _entrypointOverride) public virtual {
_onlyAdmin();
AccountCoreStorage.data().entrypointOverride = address(_entrypointOverride);
}
/*///////////////////////////////////////////////////////////////
Internal functions
//////////////////////////////////////////////////////////////*/
/// @dev Returns the salt used when deploying an Account.
function _generateSalt(address _admin, bytes memory _data) internal view virtual returns (bytes32) {
return keccak256(abi.encode(_admin, _data));
}
function getFunctionSignature(bytes calldata data) internal pure returns (bytes4 functionSelector) {
require(data.length >= 4, "!Data");
return bytes4(data[:4]);
}
function decodeExecuteCalldata(bytes calldata data) internal pure returns (address _target, uint256 _value) {
require(data.length >= 4 + 32 + 32, "!Data");
// Decode the address, which is bytes 4 to 35
_target = abi.decode(data[4:36], (address));
// Decode the value, which is bytes 36 to 68
_value = abi.decode(data[36:68], (uint256));
}
function decodeExecuteBatchCalldata(
bytes calldata data
) internal pure returns (address[] memory _targets, uint256[] memory _values, bytes[] memory _callData) {
require(data.length >= 4 + 32 + 32 + 32, "!Data");
(_targets, _values, _callData) = abi.decode(data[4:], (address[], uint256[], bytes[]));
}
/// @notice Validates the signature of a user operation.
function _validateSignature(
UserOperation calldata userOp,
bytes32 userOpHash
) internal virtual override returns (uint256 validationData) {
bytes32 hash = userOpHash.toEthSignedMessageHash();
address signer = hash.recover(userOp.signature);
if (!isValidSigner(signer, userOp)) return SIG_VALIDATION_FAILED;
SignerPermissionsStatic memory permissions = _accountPermissionsStorage().signerPermissions[signer];
uint48 validAfter = uint48(permissions.startTimestamp);
uint48 validUntil = uint48(permissions.endTimestamp);
return _packValidationData(ValidationData(address(0), validAfter, validUntil));
}
/// @notice Makes the given account an admin.
function _setAdmin(address _account, bool _isAdmin) internal virtual override {
super._setAdmin(_account, _isAdmin);
if (factory.code.length > 0) {
if (_isAdmin) {
BaseAccountFactory(factory).onSignerAdded(_account, AccountCoreStorage.data().creationSalt);
} else {
BaseAccountFactory(factory).onSignerRemoved(_account, AccountCoreStorage.data().creationSalt);
}
}
}
/// @notice Runs after every `changeRole` run.
function _afterSignerPermissionsUpdate(SignerPermissionRequest calldata _req) internal virtual override {
if (factory.code.length > 0) {
BaseAccountFactory(factory).onSignerAdded(_req.signer, AccountCoreStorage.data().creationSalt);
}
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.11;
library AccountCoreStorage {
/// @custom:storage-location erc7201:account.core.storage
/// @dev keccak256(abi.encode(uint256(keccak256("account.core.storage")) - 1)) & ~bytes32(uint256(0xff))
bytes32 public constant ACCOUNT_CORE_STORAGE_POSITION =
0x036f52c1827dab135f7fd44ca0bddde297e2f659c710e0ec53e975f22b548300;
struct Data {
address entrypointOverride;
bytes32 creationSalt;
}
function data() internal pure returns (Data storage acountCoreData) {
bytes32 position = ACCOUNT_CORE_STORAGE_POSITION;
assembly {
acountCoreData.slot := position
}
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-empty-blocks */
import "../interface/IAccount.sol";
import "../interface/IEntrypoint.sol";
import "./Helpers.sol";
/**
* Basic account implementation.
* this contract provides the basic logic for implementing the IAccount interface - validateUserOp
* specific account implementation should inherit it and provide the account-specific logic
*/
abstract contract BaseAccount is IAccount {
using UserOperationLib for UserOperation;
//return value in case of signature failure, with no time-range.
// equivalent to _packValidationData(true,0,0);
uint256 internal constant SIG_VALIDATION_FAILED = 1;
/**
* Return the account nonce.
* This method returns the next sequential nonce.
* For a nonce of a specific key, use `entrypoint.getNonce(account, key)`
*/
function getNonce() public view virtual returns (uint256) {
return entryPoint().getNonce(address(this), 0);
}
/**
* return the entryPoint used by this account.
* subclass should return the current entryPoint used by this account.
*/
function entryPoint() public view virtual returns (IEntryPoint);
/**
* Validate user's signature and nonce.
* subclass doesn't need to override this method. Instead, it should override the specific internal validation methods.
*/
function validateUserOp(
UserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external virtual override returns (uint256 validationData) {
_requireFromEntryPoint();
validationData = _validateSignature(userOp, userOpHash);
_validateNonce(userOp.nonce);
_payPrefund(missingAccountFunds);
}
/**
* ensure the request comes from the known entrypoint.
*/
function _requireFromEntryPoint() internal view virtual {
require(msg.sender == address(entryPoint()), "account: not from EntryPoint");
}
/**
* validate the signature is valid for this message.
* @param userOp validate the userOp.signature field
* @param userOpHash convenient field: the hash of the request, to check the signature against
* (also hashes the entrypoint and chain id)
* @return validationData signature and time-range of this operation
* <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure,
* otherwise, an address of an "authorizer" contract.
* <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite"
* <6-byte> validAfter - first timestamp this operation is valid
* If the account doesn't use time-range, it is enough to return SIG_VALIDATION_FAILED value (1) for signature failure.
* Note that the validation code cannot use block.timestamp (or block.number) directly.
*/
function _validateSignature(
UserOperation calldata userOp,
bytes32 userOpHash
) internal virtual returns (uint256 validationData);
/**
* Validate the nonce of the UserOperation.
* This method may validate the nonce requirement of this account.
* e.g.
* To limit the nonce to use sequenced UserOps only (no "out of order" UserOps):
* `require(nonce < type(uint64).max)`
* For a hypothetical account that *requires* the nonce to be out-of-order:
* `require(nonce & type(uint64).max == 0)`
*
* The actual nonce uniqueness is managed by the EntryPoint, and thus no other
* action is needed by the account itself.
*
* @param nonce to validate
*
* solhint-disable-next-line no-empty-blocks
*/
function _validateNonce(uint256 nonce) internal view virtual {}
/**
* sends to the entrypoint (msg.sender) the missing funds for this transaction.
* subclass MAY override this method for better funds management
* (e.g. send to the entryPoint more than the minimum required, so that in future transactions
* it will not be required to send again)
* @param missingAccountFunds the minimum value this method should send the entrypoint.
* this value MAY be zero, in case there is enough deposit, or the userOp has a paymaster.
*/
function _payPrefund(uint256 missingAccountFunds) internal virtual {
if (missingAccountFunds != 0) {
(bool success, ) = payable(msg.sender).call{ value: missingAccountFunds, gas: type(uint256).max }("");
(success);
//ignore failure (its EntryPoint's job to verify, not account.)
}
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
// Utils
import "../../../extension/Multicall.sol";
import "../../../external-deps/openzeppelin/proxy/Clones.sol";
import "../../../external-deps/openzeppelin/utils/structs/EnumerableSet.sol";
import "../utils/BaseAccount.sol";
import "../../../extension/interface/IAccountPermissions.sol";
import "../../../lib/BytesLib.sol";
// Interface
import "../interface/IEntrypoint.sol";
import "../interface/IAccountFactory.sol";
// $$\ $$\ $$\ $$\ $$\
// $$ | $$ | \__| $$ | $$ |
// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\
// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\
// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ |
// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ |
// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ |
// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/
abstract contract BaseAccountFactory is IAccountFactory, Multicall {
using EnumerableSet for EnumerableSet.AddressSet;
/*///////////////////////////////////////////////////////////////
State
//////////////////////////////////////////////////////////////*/
address public immutable accountImplementation;
address public immutable entrypoint;
EnumerableSet.AddressSet private allAccounts;
mapping(address => EnumerableSet.AddressSet) internal accountsOfSigner;
/*///////////////////////////////////////////////////////////////
Constructor
//////////////////////////////////////////////////////////////*/
constructor(address _accountImpl, address _entrypoint) {
accountImplementation = _accountImpl;
entrypoint = _entrypoint;
}
/*///////////////////////////////////////////////////////////////
External functions
//////////////////////////////////////////////////////////////*/
/// @notice Deploys a new Account for admin.
function createAccount(address _admin, bytes calldata _data) external virtual override returns (address) {
address impl = accountImplementation;
bytes32 salt = _generateSalt(_admin, _data);
address account = Clones.predictDeterministicAddress(impl, salt);
if (account.code.length > 0) {
return account;
}
account = Clones.cloneDeterministic(impl, salt);
if (msg.sender != entrypoint) {
require(allAccounts.add(account), "AccountFactory: account already registered");
}
_initializeAccount(account, _admin, _data);
emit AccountCreated(account, _admin);
return account;
}
/// @notice Callback function for an Account to register itself on the factory.
function onRegister(bytes32 _salt) external {
address account = msg.sender;
require(_isAccountOfFactory(account, _salt), "AccountFactory: not an account.");
require(allAccounts.add(account), "AccountFactory: account already registered");
}
function onSignerAdded(address _signer, bytes32 _salt) external {
address account = msg.sender;
require(_isAccountOfFactory(account, _salt), "AccountFactory: not an account.");
bool isNewSigner = accountsOfSigner[_signer].add(account);
if (isNewSigner) {
emit SignerAdded(account, _signer);
}
}
/// @notice Callback function for an Account to un-register its signers.
function onSignerRemoved(address _signer, bytes32 _salt) external {
address account = msg.sender;
require(_isAccountOfFactory(account, _salt), "AccountFactory: not an account.");
bool isAccount = accountsOfSigner[_signer].remove(account);
if (isAccount) {
emit SignerRemoved(account, _signer);
}
}
/*///////////////////////////////////////////////////////////////
View functions
//////////////////////////////////////////////////////////////*/
/// @notice Returns whether an account is registered on this factory.
function isRegistered(address _account) external view returns (bool) {
return allAccounts.contains(_account);
}
/// @notice Returns the total number of accounts.
function totalAccounts() external view returns (uint256) {
return allAccounts.length();
}
/// @notice Returns all accounts between the given indices.
function getAccounts(uint256 _start, uint256 _end) external view returns (address[] memory accounts) {
require(_start < _end && _end <= allAccounts.length(), "BaseAccountFactory: invalid indices");
uint256 len = _end - _start;
accounts = new address[](_end - _start);
for (uint256 i = 0; i < len; i += 1) {
accounts[i] = allAccounts.at(i + _start);
}
}
/// @notice Returns all accounts created on the factory.
function getAllAccounts() external view returns (address[] memory) {
return allAccounts.values();
}
/// @notice Returns the address of an Account that would be deployed with the given admin signer.
function getAddress(address _adminSigner, bytes calldata _data) public view returns (address) {
bytes32 salt = _generateSalt(_adminSigner, _data);
return Clones.predictDeterministicAddress(accountImplementation, salt);
}
/// @notice Returns all accounts that the given address is a signer of.
function getAccountsOfSigner(address signer) external view returns (address[] memory accounts) {
return accountsOfSigner[signer].values();
}
/*///////////////////////////////////////////////////////////////
Internal functions
//////////////////////////////////////////////////////////////*/
/// @dev Returns whether the caller is an account deployed by this factory.
function _isAccountOfFactory(address _account, bytes32 _salt) internal view virtual returns (bool) {
address predicted = Clones.predictDeterministicAddress(accountImplementation, _salt);
return _account == predicted;
}
function _getImplementation(address cloneAddress) internal view returns (address) {
bytes memory code = cloneAddress.code;
return BytesLib.toAddress(code, 10);
}
/// @dev Returns the salt used when deploying an Account.
function _generateSalt(address _admin, bytes memory _data) internal view virtual returns (bytes32) {
return keccak256(abi.encode(_admin, _data));
}
/// @dev Called in `createAccount`. Initializes the account contract created in `createAccount`.
function _initializeAccount(address _account, address _admin, bytes calldata _data) internal virtual;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
/* solhint-disable no-inline-assembly */
/* solhint-disable func-visibility */
/**
* returned data from validateUserOp.
* validateUserOp returns a uint256, with is created by `_packedValidationData` and parsed by `_parseValidationData`
* @param aggregator - address(0) - the account validated the signature by itself.
* address(1) - the account failed to validate the signature.
* otherwise - this is an address of a signature aggregator that must be used to validate the signature.
* @param validAfter - this UserOp is valid only after this timestamp.
* @param validaUntil - this UserOp is valid only up to this timestamp.
*/
struct ValidationData {
address aggregator;
uint48 validAfter;
uint48 validUntil;
}
//extract sigFailed, validAfter, validUntil.
// also convert zero validUntil to type(uint48).max
function _parseValidationData(uint256 validationData) pure returns (ValidationData memory data) {
address aggregator = address(uint160(validationData));
uint48 validUntil = uint48(validationData >> 160);
if (validUntil == 0) {
validUntil = type(uint48).max;
}
uint48 validAfter = uint48(validationData >> (48 + 160));
return ValidationData(aggregator, validAfter, validUntil);
}
// intersect account and paymaster ranges.
function _intersectTimeRange(
uint256 validationData,
uint256 paymasterValidationData
) pure returns (ValidationData memory) {
ValidationData memory accountValidationData = _parseValidationData(validationData);
ValidationData memory pmValidationData = _parseValidationData(paymasterValidationData);
address aggregator = accountValidationData.aggregator;
if (aggregator == address(0)) {
aggregator = pmValidationData.aggregator;
}
uint48 validAfter = accountValidationData.validAfter;
uint48 validUntil = accountValidationData.validUntil;
uint48 pmValidAfter = pmValidationData.validAfter;
uint48 pmValidUntil = pmValidationData.validUntil;
if (validAfter < pmValidAfter) validAfter = pmValidAfter;
if (validUntil > pmValidUntil) validUntil = pmValidUntil;
return ValidationData(aggregator, validAfter, validUntil);
}
/**
* helper to pack the return value for validateUserOp
* @param data - the ValidationData to pack
*/
function _packValidationData(ValidationData memory data) pure returns (uint256) {
return uint160(data.aggregator) | (uint256(data.validUntil) << 160) | (uint256(data.validAfter) << (160 + 48));
}
/**
* helper to pack the return value for validateUserOp, when not using an aggregator
* @param sigFailed - true for signature failure, false for success
* @param validUntil last timestamp this UserOperation is valid (or zero for infinite)
* @param validAfter first timestamp this UserOperation is valid
*/
function _packValidationData(bool sigFailed, uint48 validUntil, uint48 validAfter) pure returns (uint256) {
return (sigFailed ? 1 : 0) | (uint256(validUntil) << 160) | (uint256(validAfter) << (160 + 48));
}
/**
* keccak function over calldata.
* @dev copy calldata into memory, do keccak and drop allocated memory. Strangely, this is more efficient than letting solidity do it.
*/
function calldataKeccak(bytes calldata data) pure returns (bytes32 ret) {
assembly {
let mem := mload(0x40)
let len := data.length
calldatacopy(mem, data.offset, len)
ret := keccak256(mem, len)
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
/* solhint-disable no-inline-assembly */
import { calldataKeccak } from "./Helpers.sol";
/**
* User Operation struct
* @param sender the sender account of this request.
* @param nonce unique value the sender uses to verify it is not a replay.
* @param initCode if set, the account contract will be created by this constructor/
* @param callData the method call to execute on this account.
* @param callGasLimit the gas limit passed to the callData method call.
* @param verificationGasLimit gas used for validateUserOp and validatePaymasterUserOp.
* @param preVerificationGas gas not calculated by the handleOps method, but added to the gas paid. Covers batch overhead.
* @param maxFeePerGas same as EIP-1559 gas parameter.
* @param maxPriorityFeePerGas same as EIP-1559 gas parameter.
* @param paymasterAndData if set, this field holds the paymaster address and paymaster-specific data. the paymaster will pay for the transaction instead of the sender.
* @param signature sender-verified signature over the entire request, the EntryPoint address and the chain ID.
*/
struct UserOperation {
address sender;
uint256 nonce;
bytes initCode;
bytes callData;
uint256 callGasLimit;
uint256 verificationGasLimit;
uint256 preVerificationGas;
uint256 maxFeePerGas;
uint256 maxPriorityFeePerGas;
bytes paymasterAndData;
bytes signature;
}
/**
* Utility functions helpful when working with UserOperation structs.
*/
library UserOperationLib {
function getSender(UserOperation calldata userOp) internal pure returns (address) {
address data;
//read sender from userOp, which is first userOp member (saves 800 gas...)
assembly {
data := calldataload(userOp)
}
return address(uint160(data));
}
//relayer/block builder might submit the TX with higher priorityFee, but the user should not
// pay above what he signed for.
function gasPrice(UserOperation calldata userOp) internal view returns (uint256) {
unchecked {
uint256 maxFeePerGas = userOp.maxFeePerGas;
uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
if (maxFeePerGas == maxPriorityFeePerGas) {
//legacy mode (for networks that don't support basefee opcode)
return maxFeePerGas;
}
return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
}
}
function pack(UserOperation calldata userOp) internal pure returns (bytes memory ret) {
address sender = getSender(userOp);
uint256 nonce = userOp.nonce;
bytes32 hashInitCode = calldataKeccak(userOp.initCode);
bytes32 hashCallData = calldataKeccak(userOp.callData);
uint256 callGasLimit = userOp.callGasLimit;
uint256 verificationGasLimit = userOp.verificationGasLimit;
uint256 preVerificationGas = userOp.preVerificationGas;
uint256 maxFeePerGas = userOp.maxFeePerGas;
uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData);
return
abi.encode(
sender,
nonce,
hashInitCode,
hashCallData,
callGasLimit,
verificationGasLimit,
preVerificationGas,
maxFeePerGas,
maxPriorityFeePerGas,
hashPaymasterAndData
);
}
function hash(UserOperation calldata userOp) internal pure returns (bytes32) {
return keccak256(pack(userOp));
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
}{
"optimizer": {
"enabled": true,
"runs": 20
},
"evmVersion": "london",
"remappings": [
":@chainlink/=lib/chainlink/",
":@ds-test/=lib/ds-test/src/",
":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
":@rari-capital/solmate/=lib/seaport-sol/lib/seaport/lib/solmate/",
":@std/=lib/forge-std/src/",
":@thirdweb-dev/dynamic-contracts/=lib/dynamic-contracts/",
":ERC721A-Upgradeable/=lib/ERC721A-Upgradeable/contracts/",
":ERC721A/=lib/ERC721A/contracts/",
":chainlink/=lib/chainlink/contracts/",
":contracts/=contracts/",
":ds-test/=lib/ds-test/src/",
":dynamic-contracts/=lib/dynamic-contracts/src/",
":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
":erc721a-upgradeable/=lib/ERC721A-Upgradeable/",
":erc721a/=lib/ERC721A/",
":forge-std/=lib/forge-std/src/",
":lib/sstore2/=lib/dynamic-contracts/lib/sstore2/",
":murky/=lib/murky/",
":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/",
":seaport-core/=lib/seaport-core/src/",
":seaport-sol/=lib/seaport-sol/src/",
":seaport-types/=lib/seaport-types/src/",
":seaport/=lib/seaport-sol/lib/seaport/contracts/",
":solady/=lib/seaport-sol/lib/solady/",
":solarray/=lib/seaport-sol/lib/solarray/src/",
":solmate/=lib/seaport-sol/lib/seaport/lib/solmate/src/",
":sstore2/=lib/dynamic-contracts/lib/sstore2/contracts/"
],
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"signer","type":"address"},{"indexed":false,"internalType":"bool","name":"isAdmin","type":"bool"}],"name":"AdminUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"prevURI","type":"string"},{"indexed":false,"internalType":"string","name":"newURI","type":"string"}],"name":"ContractURIUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"authorizingSigner","type":"address"},{"indexed":true,"internalType":"address","name":"targetSigner","type":"address"},{"components":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"uint8","name":"isAdmin","type":"uint8"},{"internalType":"address[]","name":"approvedTargets","type":"address[]"},{"internalType":"uint256","name":"nativeTokenLimitPerTransaction","type":"uint256"},{"internalType":"uint128","name":"permissionStartTimestamp","type":"uint128"},{"internalType":"uint128","name":"permissionEndTimestamp","type":"uint128"},{"internalType":"uint128","name":"reqValidityStartTimestamp","type":"uint128"},{"internalType":"uint128","name":"reqValidityEndTimestamp","type":"uint128"},{"internalType":"bytes32","name":"uid","type":"bytes32"}],"indexed":false,"internalType":"struct IAccountPermissions.SignerPermissionRequest","name":"permissions","type":"tuple"}],"name":"SignerPermissionsUpdated","type":"event"},{"inputs":[],"name":"addDeposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_target","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"bytes","name":"_calldata","type":"bytes"}],"name":"execute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_target","type":"address[]"},{"internalType":"uint256[]","name":"_value","type":"uint256[]"},{"internalType":"bytes[]","name":"_calldata","type":"bytes[]"}],"name":"executeBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAllActiveSigners","outputs":[{"components":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"address[]","name":"approvedTargets","type":"address[]"},{"internalType":"uint256","name":"nativeTokenLimitPerTransaction","type":"uint256"},{"internalType":"uint128","name":"startTimestamp","type":"uint128"},{"internalType":"uint128","name":"endTimestamp","type":"uint128"}],"internalType":"struct IAccountPermissions.SignerPermissions[]","name":"signers","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllAdmins","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllSigners","outputs":[{"components":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"address[]","name":"approvedTargets","type":"address[]"},{"internalType":"uint256","name":"nativeTokenLimitPerTransaction","type":"uint256"},{"internalType":"uint128","name":"startTimestamp","type":"uint128"},{"internalType":"uint128","name":"endTimestamp","type":"uint128"}],"internalType":"struct IAccountPermissions.SignerPermissions[]","name":"signers","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_hash","type":"bytes32"}],"name":"getMessageHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"signer","type":"address"}],"name":"getPermissionsForSigner","outputs":[{"components":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"address[]","name":"approvedTargets","type":"address[]"},{"internalType":"uint256","name":"nativeTokenLimitPerTransaction","type":"uint256"},{"internalType":"uint128","name":"startTimestamp","type":"uint128"},{"internalType":"uint128","name":"endTimestamp","type":"uint128"}],"internalType":"struct IAccountPermissions.SignerPermissions","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"signer","type":"address"}],"name":"isActiveSigner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"isAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_hash","type":"bytes32"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"magicValue","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_uri","type":"string"}],"name":"setContractURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"uint8","name":"isAdmin","type":"uint8"},{"internalType":"address[]","name":"approvedTargets","type":"address[]"},{"internalType":"uint256","name":"nativeTokenLimitPerTransaction","type":"uint256"},{"internalType":"uint128","name":"permissionStartTimestamp","type":"uint128"},{"internalType":"uint128","name":"permissionEndTimestamp","type":"uint128"},{"internalType":"uint128","name":"reqValidityStartTimestamp","type":"uint128"},{"internalType":"uint128","name":"reqValidityEndTimestamp","type":"uint128"},{"internalType":"bytes32","name":"uid","type":"bytes32"}],"internalType":"struct IAccountPermissions.SignerPermissionRequest","name":"_req","type":"tuple"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"setPermissionsForSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"uint8","name":"isAdmin","type":"uint8"},{"internalType":"address[]","name":"approvedTargets","type":"address[]"},{"internalType":"uint256","name":"nativeTokenLimitPerTransaction","type":"uint256"},{"internalType":"uint128","name":"permissionStartTimestamp","type":"uint128"},{"internalType":"uint128","name":"permissionEndTimestamp","type":"uint128"},{"internalType":"uint128","name":"reqValidityStartTimestamp","type":"uint128"},{"internalType":"uint128","name":"reqValidityEndTimestamp","type":"uint128"},{"internalType":"bytes32","name":"uid","type":"bytes32"}],"internalType":"struct IAccountPermissions.SignerPermissionRequest","name":"req","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"verifySignerPermissionRequest","outputs":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"address","name":"signer","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"withdrawAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawDepositTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
61014060405234801561001157600080fd5b5060408051808201825260078152661058d8dbdd5b9d60ca1b6020808301918252835180850190945260018452603160f81b908401528151902060e08190527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc66101008190524660a0529192917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6100ee8184846040805160208101859052908101839052606081018290524660808201523060a082015260009060c0016040516020818303038152906040528051906020012090509392505050565b6080523060c052610120525061010392505050565b60805160a05160c05160e0516101005161012051612e076101526000396000611622015260006116710152600061164c015260006115a5015260006115cf015260006115f90152612e076000f3fe6080604052600436106100f35760003560e01c806301ffc9a7146100ff578063150b7a02146101345780631626ba7e1461017857806324d7806c14610198578063399b77da146101b857806347e1da2a146101e65780634a58db19146102085780634d44560d146102105780635892e236146102305780637dff5a79146102505780638b52d72314610270578063938e3d7b14610292578063a9082d84146102b2578063b61d27f6146102f1578063bc197c8114610311578063d42f2f351461033d578063e8a3d48514610352578063e9523c9714610374578063f15d424e14610396578063f23a6e61146103c357600080fd5b366100fa57005b600080fd5b34801561010b57600080fd5b5061011f61011a3660046120c8565b6103ef565b60405190151581526020015b60405180910390f35b34801561014057600080fd5b5061015f61014f3660046121d4565b630a85bd0160e11b949350505050565b6040516001600160e01b0319909116815260200161012b565b34801561018457600080fd5b5061015f61019336600461223f565b610435565b3480156101a457600080fd5b5061011f6101b3366004612285565b610554565b3480156101c457600080fd5b506101d86101d33660046122a2565b610583565b60405190815260200161012b565b3480156101f257600080fd5b506102066102013660046122ff565b61064e565b005b61020661080f565b34801561021c57600080fd5b5061020661022b366004612398565b6108d1565b34801561023c57600080fd5b5061020661024b366004612405565b6109a4565b34801561025c57600080fd5b5061011f61026b366004612285565b610d58565b34801561027c57600080fd5b50610285610e12565b60405161012b9190612518565b34801561029e57600080fd5b506102066102ad36600461257c565b611059565b3480156102be57600080fd5b506102d26102cd366004612405565b6110aa565b6040805192151583526001600160a01b0390911660208301520161012b565b3480156102fd57600080fd5b5061020661030c3660046125c4565b611101565b34801561031d57600080fd5b5061015f61032c366004612693565b63bc197c8160e01b95945050505050565b34801561034957600080fd5b506102856111eb565b34801561035e57600080fd5b5061036761135c565b60405161012b9190612790565b34801561038057600080fd5b506103896113f4565b60405161012b91906127a3565b3480156103a257600080fd5b506103b66103b1366004612285565b61140b565b60405161012b91906127f0565b3480156103cf57600080fd5b5061015f6103de366004612803565b63f23a6e6160e01b95945050505050565b60006001600160e01b03198216630271189760e51b148061042057506001600160e01b03198216630a85bd0160e11b145b8061042f575061042f826114e3565b92915050565b60008061044184610583565b9050600061044f8285611518565b905061045a81610554565b156104715750630b135d3f60e11b915061042f9050565b33600061047c61153c565b6001600160a01b03841660009081526006919091016020526040902090506104a48183611560565b806104d457506104b381611582565b60011480156104d4575060006104c9828261158c565b6001600160a01b0316145b6105315760405162461bcd60e51b8152602060048201526024808201527f4163636f756e743a2063616c6c6572206e6f7420617070726f7665642074617260448201526333b2ba1760e11b60648201526084015b60405180910390fd5b61053a83610d58565b1561054a57630b135d3f60e11b94505b5050505092915050565b600061055e61153c565b6001600160a01b03909216600090815260049290920160205250604090205460ff1690565b6000808260405160200161059991815260200190565b60405160208183030381529060405280519060200120905060007f82cac545155fcbf147f2a9013809613677ac7d65498556e6d19ce43bcbf6c284826040516020016105ef929190918252602082015260400190565b60405160208183030381529060405280519060200120905061060f611598565b60405161190160f01b60208201526022810191909152604281018290526062016040516020818303038152906040528051906020012092505050919050565b306001600160a01b031663b0d691fe6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561068c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106b0919061286b565b6001600160a01b0316336001600160a01b031614806106d357506106d333610554565b6106ef5760405162461bcd60e51b815260040161052890612888565b6106f76116bf565b848114801561070557508483145b6107515760405162461bcd60e51b815260206004820152601d60248201527f4163636f756e743a2077726f6e67206172726179206c656e677468732e0000006044820152606401610528565b60005b85811015610806576107fd878783818110610771576107716128c9565b90506020020160208101906107869190612285565b868684818110610798576107986128c9565b905060200201358585858181106107b1576107b16128c9565b90506020028101906107c391906128df565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506117ed92505050565b50600101610754565b50505050505050565b306001600160a01b031663b0d691fe6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561084d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610871919061286b565b6001600160a01b031663b760faf934306040518363ffffffff1660e01b815260040161089d9190612925565b6000604051808303818588803b1580156108b657600080fd5b505af11580156108ca573d6000803e3d6000fd5b5050505050565b6108d961185e565b306001600160a01b031663b0d691fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610917573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061093b919061286b565b60405163040b850f60e31b81526001600160a01b03848116600483015260248201849052919091169063205c2878906044015b600060405180830381600087803b15801561098857600080fd5b505af115801561099c573d6000803e3d6000fd5b505050505050565b60006109b36020850185612285565b9050426109c660e0860160c08701612950565b6001600160801b0316111580156109f557506109e9610100850160e08601612950565b6001600160801b031642105b610a2b5760405162461bcd60e51b8152602060048201526007602482015266085c195c9a5bd960ca1b6044820152606401610528565b600080610a398686866110aa565b9150915081610a735760405162461bcd60e51b8152600401610528906020808252600490820152632173696760e01b604082015260600190565b6001610a7d61153c565b610100880135600090815260079190910160209081526040808320805460ff1916941515949094179093559091610ab99190890190890161297c565b60ff161115610ae6576000610ad4604088016020890161297c565b60ff166001149050610806848261189e565b610aef83610554565b15610b245760405162461bcd60e51b815260206004820152600560248201526430b236b4b760d91b6044820152606401610528565b610b3983610b3061153c565b6002019061194d565b50604051806060016040528087606001358152602001876080016020810190610b629190612950565b6001600160801b03168152602001610b8060c0890160a08a01612950565b6001600160801b03169052610b9361153c565b6001600160a01b03851660009081526005919091016020908152604080832084518155918401519301516001600160801b03908116600160801b02931692909217600190920191909155610c09610be861153c565b6001600160a01b038616600090815260069190910160205260409020611962565b805190915060005b81811015610c7357610c60838281518110610c2e57610c2e6128c9565b6020026020010151610c3e61153c565b6001600160a01b0389166000908152600691909101602052604090209061196f565b50610c6c6001826129ad565b9050610c11565b50610c8160408901896129c0565b9050905060005b81811015610d0257610cef610ca060408b018b6129c0565b83818110610cb057610cb06128c9565b9050602002016020810190610cc59190612285565b610ccd61153c565b6001600160a01b0389166000908152600691909101602052604090209061194d565b50610cfb6001826129ad565b9050610c88565b50846001600160a01b0316836001600160a01b03167ff21d10c26e35863a8df291aca54181ee8c4a3bc8e00246c3f7a5a14b69d826a78a604051610d469190612a9a565b60405180910390a35050505050505050565b600080610d6361153c565b6001600160a01b038416600090815260059190910160209081526040918290208251606081018452815481526001909101546001600160801b03808216938301849052600160801b90910416928101929092529091504210801590610dd4575080604001516001600160801b031642105b8015610e0b57506000610e09610de861153c565b6001600160a01b038616600090815260069190910160205260409020611582565b115b9392505050565b60606000610e29610e2161153c565b600201611962565b80519091506000805b82811015610eba57610e5c848281518110610e4f57610e4f6128c9565b6020026020010151610d58565b15610e735781610e6b81612b85565b925050610ea8565b6000848281518110610e8757610e876128c9565b60200260200101906001600160a01b031690816001600160a01b0316815250505b610eb36001826129ad565b9050610e32565b50806001600160401b03811115610ed357610ed3612117565b604051908082528060200260200182016040528015610f0c57816020015b610ef961207e565b815260200190600190039081610ef15790505b5093506000805b838110156110515760006001600160a01b0316858281518110610f3857610f386128c9565b60200260200101516001600160a01b03161461103f576000858281518110610f6257610f626128c9565b602002602001015190506000610f7661153c565b6001600160a01b038316600081815260059290920160209081526040928390208351606081018552815481526001909101546001600160801b0380821683850152600160801b9091041681850152835160a081019094529183529092508101610fe0610be861153c565b81526020018260000151815260200182602001516001600160801b0316815260200182604001516001600160801b031681525088858061101f90612b85565b965081518110611031576110316128c9565b602002602001018190525050505b61104a6001826129ad565b9050610f13565b505050505090565b611061611984565b61109e5760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606401610528565b6110a78161199c565b50565b6000806110c06110b986611a83565b8585611bc7565b90506110ca61153c565b6101008601356000908152600791909101602052604090205460ff161580156110f757506110f781610554565b9150935093915050565b306001600160a01b031663b0d691fe6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561113f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611163919061286b565b6001600160a01b0316336001600160a01b03161480611186575061118633610554565b6111a25760405162461bcd60e51b815260040161052890612888565b6111aa6116bf565b6108ca848484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506117ed92505050565b606060006111fa610e2161153c565b8051909150806001600160401b0381111561121757611217612117565b60405190808252806020026020018201604052801561125057816020015b61123d61207e565b8152602001906001900390816112355790505b50925060005b81811015611356576000838281518110611272576112726128c9565b60200260200101519050600061128661153c565b6001600160a01b038316600081815260059290920160209081526040928390208351606081018552815481526001909101546001600160801b0380821683850152600160801b9091041681850152835160a0810190945291835290925081016112f0610be861153c565b81526020018260000151815260200182602001516001600160801b0316815260200182604001516001600160801b0316815250868481518110611335576113356128c9565b6020026020010181905250505060018161134f91906129ad565b9050611256565b50505090565b6060611366611c21565b805461137190612b9e565b80601f016020809104026020016040519081016040528092919081815260200182805461139d90612b9e565b80156113ea5780601f106113bf576101008083540402835291602001916113ea565b820191906000526020600020905b8154815290600101906020018083116113cd57829003601f168201915b5050505050905090565b606061140661140161153c565b611962565b905090565b61141361207e565b600061141d61153c565b6001600160a01b038416600081815260059290920160209081526040928390208351606081018552815481526001909101546001600160801b0380821683850152600160801b9091041681850152835160a0810190945291835290925081016114a861148761153c565b6001600160a01b038716600090815260069190910160205260409020611962565b81526020018260000151815260200182602001516001600160801b0316815260200182604001516001600160801b0316815250915050919050565b60006001600160e01b03198216630271189760e51b148061042f57506301ffc9a760e01b6001600160e01b031983161461042f565b60008060006115278585611c45565b9150915061153481611c8a565b509392505050565b7f3181e78fc1b109bc611fd2406150bf06e33faa75f71cba12c3e1fd670f2def0090565b6001600160a01b03811660009081526001830160205260408120541515610e0b565b600061042f825490565b6000610e0b8383611dcf565b6000306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480156115f157507f000000000000000000000000000000000000000000000000000000000000000046145b1561161b57507f000000000000000000000000000000000000000000000000000000000000000090565b50604080517f00000000000000000000000000000000000000000000000000000000000000006020808301919091527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b6000306001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116ff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611723919061286b565b60405163c3c5a54760e01b815290915081906001600160a01b0382169063c3c5a54790611754903090600401612925565b602060405180830381865afa158015611771573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117959190612bd8565b6117e9577f036f52c1827dab135f7fd44ca0bddde297e2f659c710e0ec53e975f22b548301546040516320e80fe360e21b815260048101919091526001600160a01b038216906383a03f8c9060240161096e565b5050565b60606000846001600160a01b0316848460405161180a9190612bfa565b60006040518083038185875af1925050503d8060008114611847576040519150601f19603f3d011682016040523d82523d6000602084013e61184c565b606091505b50925090508061153457815160208301fd5b61186733610554565b61189c5760405162461bcd60e51b815260206004820152600660248201526510b0b236b4b760d11b6044820152606401610528565b565b806118a761153c565b6001600160a01b038416600090815260049190910160205260409020805460ff191691151591909117905580156118f0576118ea826118e461153c565b9061194d565b50611904565b611902826118fc61153c565b9061196f565b505b816001600160a01b03167f235bc17e7930760029e9f4d860a2a8089976de5b381cf8380fc11c1d88a1113382604051611941911515815260200190565b60405180910390a25050565b6000610e0b836001600160a01b038416611df9565b60606000610e0b83611e48565b6000610e0b836001600160a01b038416611ea4565b600061198f33610554565b8061140657505030331490565b60006119a6611c21565b80546119b190612b9e565b80601f01602080910402602001604051908101604052809291908181526020018280546119dd90612b9e565b8015611a2a5780601f106119ff57610100808354040283529160200191611a2a565b820191906000526020600020905b815481529060010190602001808311611a0d57829003601f168201915b5050505050905081611a3a611c21565b90611a459082612c63565b507fc9c7c3fe08b88b4df9d4d47ef47d2c43d55c025a0ba88ca442580ed9e7348a168183604051611a77929190612d22565b60405180910390a15050565b60607f3fd4a1a1a267c84185e3b7eecd57c68783c0581d538b9d6e5f23e4670497c1e9611ab36020840184612285565b611ac3604085016020860161297c565b611ad060408601866129c0565b604051602001611ae1929190612d50565b60408051601f1981840301815291905280516020909101206060860135611b0e60a0880160808901612950565b611b1e60c0890160a08a01612950565b611b2e60e08a0160c08b01612950565b611b3f6101008b0160e08c01612950565b60408051602081019a909a526001600160a01b039098169789019790975260ff9095166060880152608087019390935260a08601919091526001600160801b0390811660c086015290811660e0850152908116610100848101919091529116610120830152830135610140820152610160016040516020818303038152906040529050919050565b6000611c1983838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505087516020890120611c1392509050611f97565b90611518565b949350505050565b7f4bc804ba64359c0e35e5ed5d90ee596ecaa49a3a930ddcb1470ea0dd625da90090565b6000808251604103611c7b5760208301516040840151606085015160001a611c6f87828585611fc4565b94509450505050611c83565b506000905060025b9250929050565b6000816004811115611c9e57611c9e612d92565b03611ca65750565b6001816004811115611cba57611cba612d92565b03611d025760405162461bcd60e51b815260206004820152601860248201527745434453413a20696e76616c6964207369676e617475726560401b6044820152606401610528565b6002816004811115611d1657611d16612d92565b03611d635760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610528565b6003816004811115611d7757611d77612d92565b036110a75760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610528565b6000826000018281548110611de657611de66128c9565b9060005260206000200154905092915050565b6000818152600183016020526040812054611e405750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561042f565b50600061042f565b606081600001805480602002602001604051908101604052809291908181526020018280548015611e9857602002820191906000526020600020905b815481526020019060010190808311611e84575b50505050509050919050565b60008181526001830160205260408120548015611f8d576000611ec8600183612da8565b8554909150600090611edc90600190612da8565b9050818114611f41576000866000018281548110611efc57611efc6128c9565b9060005260206000200154905080876000018481548110611f1f57611f1f6128c9565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080611f5257611f52612dbb565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061042f565b600091505061042f565b600061042f611fa4611598565b8360405161190160f01b8152600281019290925260228201526042902090565b6000806fa2a8918ca85bafe22016d0b997e4df60600160ff1b03831115611ff15750600090506003612075565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015612045573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661206e57600060019250925050612075565b9150600090505b94509492505050565b6040518060a0016040528060006001600160a01b03168152602001606081526020016000815260200160006001600160801b0316815260200160006001600160801b031681525090565b6000602082840312156120da57600080fd5b81356001600160e01b031981168114610e0b57600080fd5b6001600160a01b03811681146110a757600080fd5b8035612112816120f2565b919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561215557612155612117565b604052919050565b60006001600160401b0383111561217657612176612117565b612189601f8401601f191660200161212d565b905082815283838301111561219d57600080fd5b828260208301376000602084830101529392505050565b600082601f8301126121c557600080fd5b610e0b8383356020850161215d565b600080600080608085870312156121ea57600080fd5b84356121f5816120f2565b93506020850135612205816120f2565b92506040850135915060608501356001600160401b0381111561222757600080fd5b612233878288016121b4565b91505092959194509250565b6000806040838503121561225257600080fd5b8235915060208301356001600160401b0381111561226f57600080fd5b61227b858286016121b4565b9150509250929050565b60006020828403121561229757600080fd5b8135610e0b816120f2565b6000602082840312156122b457600080fd5b5035919050565b60008083601f8401126122cd57600080fd5b5081356001600160401b038111156122e457600080fd5b6020830191508360208260051b8501011115611c8357600080fd5b6000806000806000806060878903121561231857600080fd5b86356001600160401b038082111561232f57600080fd5b61233b8a838b016122bb565b9098509650602089013591508082111561235457600080fd5b6123608a838b016122bb565b9096509450604089013591508082111561237957600080fd5b5061238689828a016122bb565b979a9699509497509295939492505050565b600080604083850312156123ab57600080fd5b82356123b6816120f2565b946020939093013593505050565b60008083601f8401126123d657600080fd5b5081356001600160401b038111156123ed57600080fd5b602083019150836020828501011115611c8357600080fd5b60008060006040848603121561241a57600080fd5b83356001600160401b038082111561243157600080fd5b90850190610120828803121561244657600080fd5b9093506020850135908082111561245c57600080fd5b50612469868287016123c4565b9497909650939450505050565b6001600160801b03169052565b80516001600160a01b03908116835260208083015160a082860181905281519086018190526000939183019290849060c08801905b808310156124da578551851682529483019460019290920191908301906124b8565b5060408701516040890152606087015194506124f96060890186612476565b6080870151945061250d6080890186612476565b979650505050505050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b8281101561256f57603f1988860301845261255d858351612483565b94509285019290850190600101612541565b5092979650505050505050565b60006020828403121561258e57600080fd5b81356001600160401b038111156125a457600080fd5b8201601f810184136125b557600080fd5b611c198482356020840161215d565b600080600080606085870312156125da57600080fd5b84356125e5816120f2565b93506020850135925060408501356001600160401b0381111561260757600080fd5b612613878288016123c4565b95989497509550505050565b600082601f83011261263057600080fd5b813560206001600160401b0382111561264b5761264b612117565b8160051b61265a82820161212d565b928352848101820192828101908785111561267457600080fd5b83870192505b8483101561250d5782358252918301919083019061267a565b600080600080600060a086880312156126ab57600080fd5b85356126b6816120f2565b945060208601356126c6816120f2565b935060408601356001600160401b03808211156126e257600080fd5b6126ee89838a0161261f565b9450606088013591508082111561270457600080fd5b61271089838a0161261f565b9350608088013591508082111561272657600080fd5b50612733888289016121b4565b9150509295509295909350565b60005b8381101561275b578181015183820152602001612743565b50506000910152565b6000815180845261277c816020860160208601612740565b601f01601f19169290920160200192915050565b602081526000610e0b6020830184612764565b6020808252825182820181905260009190848201906040850190845b818110156127e45783516001600160a01b0316835292840192918401916001016127bf565b50909695505050505050565b602081526000610e0b6020830184612483565b600080600080600060a0868803121561281b57600080fd5b8535612826816120f2565b94506020860135612836816120f2565b9350604086013592506060860135915060808601356001600160401b0381111561285f57600080fd5b612733888289016121b4565b60006020828403121561287d57600080fd5b8151610e0b816120f2565b60208082526021908201527f4163636f756e743a206e6f742061646d696e206f7220456e747279506f696e746040820152601760f91b606082015260800190565b634e487b7160e01b600052603260045260246000fd5b6000808335601e198436030181126128f657600080fd5b8301803591506001600160401b0382111561291057600080fd5b602001915036819003821315611c8357600080fd5b6001600160a01b0391909116815260200190565b80356001600160801b038116811461211257600080fd5b60006020828403121561296257600080fd5b610e0b82612939565b803560ff8116811461211257600080fd5b60006020828403121561298e57600080fd5b610e0b8261296b565b634e487b7160e01b600052601160045260246000fd5b8082018082111561042f5761042f612997565b6000808335601e198436030181126129d757600080fd5b8301803591506001600160401b038211156129f157600080fd5b6020019150600581901b3603821315611c8357600080fd5b6000808335601e19843603018112612a2057600080fd5b83016020810192503590506001600160401b03811115612a3f57600080fd5b8060051b3603821315611c8357600080fd5b8183526000602080850194508260005b85811015612a8f578135612a74816120f2565b6001600160a01b031687529582019590820190600101612a61565b509495945050505050565b60208152612abb60208201612aae84612107565b6001600160a01b03169052565b6000612ac96020840161296b565b60ff8116604084015250612ae06040840184612a09565b610120806060860152612af861014086018385612a51565b925060608601356080860152612b1060808701612939565b9150612b1f60a0860183612476565b612b2b60a08701612939565b9150612b3a60c0860183612476565b612b4660c08701612939565b9150612b5560e0860183612476565b612b6160e08701612939565b9150610100612b7281870184612476565b9590950135939094019290925250919050565b600060018201612b9757612b97612997565b5060010190565b600181811c90821680612bb257607f821691505b602082108103612bd257634e487b7160e01b600052602260045260246000fd5b50919050565b600060208284031215612bea57600080fd5b81518015158114610e0b57600080fd5b60008251612c0c818460208701612740565b9190910192915050565b601f821115612c5e576000816000526020600020601f850160051c81016020861015612c3f5750805b601f850160051c820191505b8181101561099c57828155600101612c4b565b505050565b81516001600160401b03811115612c7c57612c7c612117565b612c9081612c8a8454612b9e565b84612c16565b602080601f831160018114612cc55760008415612cad5750858301515b600019600386901b1c1916600185901b17855561099c565b600085815260208120601f198616915b82811015612cf457888601518255948401946001909101908401612cd5565b5085821015612d125787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b604081526000612d356040830185612764565b8281036020840152612d478185612764565b95945050505050565b60008184825b85811015612d87578135612d69816120f2565b6001600160a01b031683526020928301929190910190600101612d56565b509095945050505050565b634e487b7160e01b600052602160045260246000fd5b8181038181111561042f5761042f612997565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220597237df4b3d5e31eb695964a10aa951c1d2e74f250fd8cbb7111b181659042f64736f6c63430008170033
Deployed Bytecode
0x6080604052600436106100f35760003560e01c806301ffc9a7146100ff578063150b7a02146101345780631626ba7e1461017857806324d7806c14610198578063399b77da146101b857806347e1da2a146101e65780634a58db19146102085780634d44560d146102105780635892e236146102305780637dff5a79146102505780638b52d72314610270578063938e3d7b14610292578063a9082d84146102b2578063b61d27f6146102f1578063bc197c8114610311578063d42f2f351461033d578063e8a3d48514610352578063e9523c9714610374578063f15d424e14610396578063f23a6e61146103c357600080fd5b366100fa57005b600080fd5b34801561010b57600080fd5b5061011f61011a3660046120c8565b6103ef565b60405190151581526020015b60405180910390f35b34801561014057600080fd5b5061015f61014f3660046121d4565b630a85bd0160e11b949350505050565b6040516001600160e01b0319909116815260200161012b565b34801561018457600080fd5b5061015f61019336600461223f565b610435565b3480156101a457600080fd5b5061011f6101b3366004612285565b610554565b3480156101c457600080fd5b506101d86101d33660046122a2565b610583565b60405190815260200161012b565b3480156101f257600080fd5b506102066102013660046122ff565b61064e565b005b61020661080f565b34801561021c57600080fd5b5061020661022b366004612398565b6108d1565b34801561023c57600080fd5b5061020661024b366004612405565b6109a4565b34801561025c57600080fd5b5061011f61026b366004612285565b610d58565b34801561027c57600080fd5b50610285610e12565b60405161012b9190612518565b34801561029e57600080fd5b506102066102ad36600461257c565b611059565b3480156102be57600080fd5b506102d26102cd366004612405565b6110aa565b6040805192151583526001600160a01b0390911660208301520161012b565b3480156102fd57600080fd5b5061020661030c3660046125c4565b611101565b34801561031d57600080fd5b5061015f61032c366004612693565b63bc197c8160e01b95945050505050565b34801561034957600080fd5b506102856111eb565b34801561035e57600080fd5b5061036761135c565b60405161012b9190612790565b34801561038057600080fd5b506103896113f4565b60405161012b91906127a3565b3480156103a257600080fd5b506103b66103b1366004612285565b61140b565b60405161012b91906127f0565b3480156103cf57600080fd5b5061015f6103de366004612803565b63f23a6e6160e01b95945050505050565b60006001600160e01b03198216630271189760e51b148061042057506001600160e01b03198216630a85bd0160e11b145b8061042f575061042f826114e3565b92915050565b60008061044184610583565b9050600061044f8285611518565b905061045a81610554565b156104715750630b135d3f60e11b915061042f9050565b33600061047c61153c565b6001600160a01b03841660009081526006919091016020526040902090506104a48183611560565b806104d457506104b381611582565b60011480156104d4575060006104c9828261158c565b6001600160a01b0316145b6105315760405162461bcd60e51b8152602060048201526024808201527f4163636f756e743a2063616c6c6572206e6f7420617070726f7665642074617260448201526333b2ba1760e11b60648201526084015b60405180910390fd5b61053a83610d58565b1561054a57630b135d3f60e11b94505b5050505092915050565b600061055e61153c565b6001600160a01b03909216600090815260049290920160205250604090205460ff1690565b6000808260405160200161059991815260200190565b60405160208183030381529060405280519060200120905060007f82cac545155fcbf147f2a9013809613677ac7d65498556e6d19ce43bcbf6c284826040516020016105ef929190918252602082015260400190565b60405160208183030381529060405280519060200120905061060f611598565b60405161190160f01b60208201526022810191909152604281018290526062016040516020818303038152906040528051906020012092505050919050565b306001600160a01b031663b0d691fe6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561068c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106b0919061286b565b6001600160a01b0316336001600160a01b031614806106d357506106d333610554565b6106ef5760405162461bcd60e51b815260040161052890612888565b6106f76116bf565b848114801561070557508483145b6107515760405162461bcd60e51b815260206004820152601d60248201527f4163636f756e743a2077726f6e67206172726179206c656e677468732e0000006044820152606401610528565b60005b85811015610806576107fd878783818110610771576107716128c9565b90506020020160208101906107869190612285565b868684818110610798576107986128c9565b905060200201358585858181106107b1576107b16128c9565b90506020028101906107c391906128df565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506117ed92505050565b50600101610754565b50505050505050565b306001600160a01b031663b0d691fe6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561084d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610871919061286b565b6001600160a01b031663b760faf934306040518363ffffffff1660e01b815260040161089d9190612925565b6000604051808303818588803b1580156108b657600080fd5b505af11580156108ca573d6000803e3d6000fd5b5050505050565b6108d961185e565b306001600160a01b031663b0d691fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610917573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061093b919061286b565b60405163040b850f60e31b81526001600160a01b03848116600483015260248201849052919091169063205c2878906044015b600060405180830381600087803b15801561098857600080fd5b505af115801561099c573d6000803e3d6000fd5b505050505050565b60006109b36020850185612285565b9050426109c660e0860160c08701612950565b6001600160801b0316111580156109f557506109e9610100850160e08601612950565b6001600160801b031642105b610a2b5760405162461bcd60e51b8152602060048201526007602482015266085c195c9a5bd960ca1b6044820152606401610528565b600080610a398686866110aa565b9150915081610a735760405162461bcd60e51b8152600401610528906020808252600490820152632173696760e01b604082015260600190565b6001610a7d61153c565b610100880135600090815260079190910160209081526040808320805460ff1916941515949094179093559091610ab99190890190890161297c565b60ff161115610ae6576000610ad4604088016020890161297c565b60ff166001149050610806848261189e565b610aef83610554565b15610b245760405162461bcd60e51b815260206004820152600560248201526430b236b4b760d91b6044820152606401610528565b610b3983610b3061153c565b6002019061194d565b50604051806060016040528087606001358152602001876080016020810190610b629190612950565b6001600160801b03168152602001610b8060c0890160a08a01612950565b6001600160801b03169052610b9361153c565b6001600160a01b03851660009081526005919091016020908152604080832084518155918401519301516001600160801b03908116600160801b02931692909217600190920191909155610c09610be861153c565b6001600160a01b038616600090815260069190910160205260409020611962565b805190915060005b81811015610c7357610c60838281518110610c2e57610c2e6128c9565b6020026020010151610c3e61153c565b6001600160a01b0389166000908152600691909101602052604090209061196f565b50610c6c6001826129ad565b9050610c11565b50610c8160408901896129c0565b9050905060005b81811015610d0257610cef610ca060408b018b6129c0565b83818110610cb057610cb06128c9565b9050602002016020810190610cc59190612285565b610ccd61153c565b6001600160a01b0389166000908152600691909101602052604090209061194d565b50610cfb6001826129ad565b9050610c88565b50846001600160a01b0316836001600160a01b03167ff21d10c26e35863a8df291aca54181ee8c4a3bc8e00246c3f7a5a14b69d826a78a604051610d469190612a9a565b60405180910390a35050505050505050565b600080610d6361153c565b6001600160a01b038416600090815260059190910160209081526040918290208251606081018452815481526001909101546001600160801b03808216938301849052600160801b90910416928101929092529091504210801590610dd4575080604001516001600160801b031642105b8015610e0b57506000610e09610de861153c565b6001600160a01b038616600090815260069190910160205260409020611582565b115b9392505050565b60606000610e29610e2161153c565b600201611962565b80519091506000805b82811015610eba57610e5c848281518110610e4f57610e4f6128c9565b6020026020010151610d58565b15610e735781610e6b81612b85565b925050610ea8565b6000848281518110610e8757610e876128c9565b60200260200101906001600160a01b031690816001600160a01b0316815250505b610eb36001826129ad565b9050610e32565b50806001600160401b03811115610ed357610ed3612117565b604051908082528060200260200182016040528015610f0c57816020015b610ef961207e565b815260200190600190039081610ef15790505b5093506000805b838110156110515760006001600160a01b0316858281518110610f3857610f386128c9565b60200260200101516001600160a01b03161461103f576000858281518110610f6257610f626128c9565b602002602001015190506000610f7661153c565b6001600160a01b038316600081815260059290920160209081526040928390208351606081018552815481526001909101546001600160801b0380821683850152600160801b9091041681850152835160a081019094529183529092508101610fe0610be861153c565b81526020018260000151815260200182602001516001600160801b0316815260200182604001516001600160801b031681525088858061101f90612b85565b965081518110611031576110316128c9565b602002602001018190525050505b61104a6001826129ad565b9050610f13565b505050505090565b611061611984565b61109e5760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606401610528565b6110a78161199c565b50565b6000806110c06110b986611a83565b8585611bc7565b90506110ca61153c565b6101008601356000908152600791909101602052604090205460ff161580156110f757506110f781610554565b9150935093915050565b306001600160a01b031663b0d691fe6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561113f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611163919061286b565b6001600160a01b0316336001600160a01b03161480611186575061118633610554565b6111a25760405162461bcd60e51b815260040161052890612888565b6111aa6116bf565b6108ca848484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506117ed92505050565b606060006111fa610e2161153c565b8051909150806001600160401b0381111561121757611217612117565b60405190808252806020026020018201604052801561125057816020015b61123d61207e565b8152602001906001900390816112355790505b50925060005b81811015611356576000838281518110611272576112726128c9565b60200260200101519050600061128661153c565b6001600160a01b038316600081815260059290920160209081526040928390208351606081018552815481526001909101546001600160801b0380821683850152600160801b9091041681850152835160a0810190945291835290925081016112f0610be861153c565b81526020018260000151815260200182602001516001600160801b0316815260200182604001516001600160801b0316815250868481518110611335576113356128c9565b6020026020010181905250505060018161134f91906129ad565b9050611256565b50505090565b6060611366611c21565b805461137190612b9e565b80601f016020809104026020016040519081016040528092919081815260200182805461139d90612b9e565b80156113ea5780601f106113bf576101008083540402835291602001916113ea565b820191906000526020600020905b8154815290600101906020018083116113cd57829003601f168201915b5050505050905090565b606061140661140161153c565b611962565b905090565b61141361207e565b600061141d61153c565b6001600160a01b038416600081815260059290920160209081526040928390208351606081018552815481526001909101546001600160801b0380821683850152600160801b9091041681850152835160a0810190945291835290925081016114a861148761153c565b6001600160a01b038716600090815260069190910160205260409020611962565b81526020018260000151815260200182602001516001600160801b0316815260200182604001516001600160801b0316815250915050919050565b60006001600160e01b03198216630271189760e51b148061042f57506301ffc9a760e01b6001600160e01b031983161461042f565b60008060006115278585611c45565b9150915061153481611c8a565b509392505050565b7f3181e78fc1b109bc611fd2406150bf06e33faa75f71cba12c3e1fd670f2def0090565b6001600160a01b03811660009081526001830160205260408120541515610e0b565b600061042f825490565b6000610e0b8383611dcf565b6000306001600160a01b037f000000000000000000000000016b3149727d1d9b12471bc256b2f6a6e07282c9161480156115f157507f00000000000000000000000000000000000000000000000000000000000b67d246145b1561161b57507fbf522dc65996dae8543c3a545fc079dd1c5752115bf914d1d4f0ec30fe8c1a1f90565b50604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6020808301919091527ff0729608244859f656d32ae4cbc6b0367695d68d8e941a28f5e2d33c6d5182dd828401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b6000306001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116ff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611723919061286b565b60405163c3c5a54760e01b815290915081906001600160a01b0382169063c3c5a54790611754903090600401612925565b602060405180830381865afa158015611771573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117959190612bd8565b6117e9577f036f52c1827dab135f7fd44ca0bddde297e2f659c710e0ec53e975f22b548301546040516320e80fe360e21b815260048101919091526001600160a01b038216906383a03f8c9060240161096e565b5050565b60606000846001600160a01b0316848460405161180a9190612bfa565b60006040518083038185875af1925050503d8060008114611847576040519150601f19603f3d011682016040523d82523d6000602084013e61184c565b606091505b50925090508061153457815160208301fd5b61186733610554565b61189c5760405162461bcd60e51b815260206004820152600660248201526510b0b236b4b760d11b6044820152606401610528565b565b806118a761153c565b6001600160a01b038416600090815260049190910160205260409020805460ff191691151591909117905580156118f0576118ea826118e461153c565b9061194d565b50611904565b611902826118fc61153c565b9061196f565b505b816001600160a01b03167f235bc17e7930760029e9f4d860a2a8089976de5b381cf8380fc11c1d88a1113382604051611941911515815260200190565b60405180910390a25050565b6000610e0b836001600160a01b038416611df9565b60606000610e0b83611e48565b6000610e0b836001600160a01b038416611ea4565b600061198f33610554565b8061140657505030331490565b60006119a6611c21565b80546119b190612b9e565b80601f01602080910402602001604051908101604052809291908181526020018280546119dd90612b9e565b8015611a2a5780601f106119ff57610100808354040283529160200191611a2a565b820191906000526020600020905b815481529060010190602001808311611a0d57829003601f168201915b5050505050905081611a3a611c21565b90611a459082612c63565b507fc9c7c3fe08b88b4df9d4d47ef47d2c43d55c025a0ba88ca442580ed9e7348a168183604051611a77929190612d22565b60405180910390a15050565b60607f3fd4a1a1a267c84185e3b7eecd57c68783c0581d538b9d6e5f23e4670497c1e9611ab36020840184612285565b611ac3604085016020860161297c565b611ad060408601866129c0565b604051602001611ae1929190612d50565b60408051601f1981840301815291905280516020909101206060860135611b0e60a0880160808901612950565b611b1e60c0890160a08a01612950565b611b2e60e08a0160c08b01612950565b611b3f6101008b0160e08c01612950565b60408051602081019a909a526001600160a01b039098169789019790975260ff9095166060880152608087019390935260a08601919091526001600160801b0390811660c086015290811660e0850152908116610100848101919091529116610120830152830135610140820152610160016040516020818303038152906040529050919050565b6000611c1983838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505087516020890120611c1392509050611f97565b90611518565b949350505050565b7f4bc804ba64359c0e35e5ed5d90ee596ecaa49a3a930ddcb1470ea0dd625da90090565b6000808251604103611c7b5760208301516040840151606085015160001a611c6f87828585611fc4565b94509450505050611c83565b506000905060025b9250929050565b6000816004811115611c9e57611c9e612d92565b03611ca65750565b6001816004811115611cba57611cba612d92565b03611d025760405162461bcd60e51b815260206004820152601860248201527745434453413a20696e76616c6964207369676e617475726560401b6044820152606401610528565b6002816004811115611d1657611d16612d92565b03611d635760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610528565b6003816004811115611d7757611d77612d92565b036110a75760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610528565b6000826000018281548110611de657611de66128c9565b9060005260206000200154905092915050565b6000818152600183016020526040812054611e405750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561042f565b50600061042f565b606081600001805480602002602001604051908101604052809291908181526020018280548015611e9857602002820191906000526020600020905b815481526020019060010190808311611e84575b50505050509050919050565b60008181526001830160205260408120548015611f8d576000611ec8600183612da8565b8554909150600090611edc90600190612da8565b9050818114611f41576000866000018281548110611efc57611efc6128c9565b9060005260206000200154905080876000018481548110611f1f57611f1f6128c9565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080611f5257611f52612dbb565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061042f565b600091505061042f565b600061042f611fa4611598565b8360405161190160f01b8152600281019290925260228201526042902090565b6000806fa2a8918ca85bafe22016d0b997e4df60600160ff1b03831115611ff15750600090506003612075565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015612045573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661206e57600060019250925050612075565b9150600090505b94509492505050565b6040518060a0016040528060006001600160a01b03168152602001606081526020016000815260200160006001600160801b0316815260200160006001600160801b031681525090565b6000602082840312156120da57600080fd5b81356001600160e01b031981168114610e0b57600080fd5b6001600160a01b03811681146110a757600080fd5b8035612112816120f2565b919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561215557612155612117565b604052919050565b60006001600160401b0383111561217657612176612117565b612189601f8401601f191660200161212d565b905082815283838301111561219d57600080fd5b828260208301376000602084830101529392505050565b600082601f8301126121c557600080fd5b610e0b8383356020850161215d565b600080600080608085870312156121ea57600080fd5b84356121f5816120f2565b93506020850135612205816120f2565b92506040850135915060608501356001600160401b0381111561222757600080fd5b612233878288016121b4565b91505092959194509250565b6000806040838503121561225257600080fd5b8235915060208301356001600160401b0381111561226f57600080fd5b61227b858286016121b4565b9150509250929050565b60006020828403121561229757600080fd5b8135610e0b816120f2565b6000602082840312156122b457600080fd5b5035919050565b60008083601f8401126122cd57600080fd5b5081356001600160401b038111156122e457600080fd5b6020830191508360208260051b8501011115611c8357600080fd5b6000806000806000806060878903121561231857600080fd5b86356001600160401b038082111561232f57600080fd5b61233b8a838b016122bb565b9098509650602089013591508082111561235457600080fd5b6123608a838b016122bb565b9096509450604089013591508082111561237957600080fd5b5061238689828a016122bb565b979a9699509497509295939492505050565b600080604083850312156123ab57600080fd5b82356123b6816120f2565b946020939093013593505050565b60008083601f8401126123d657600080fd5b5081356001600160401b038111156123ed57600080fd5b602083019150836020828501011115611c8357600080fd5b60008060006040848603121561241a57600080fd5b83356001600160401b038082111561243157600080fd5b90850190610120828803121561244657600080fd5b9093506020850135908082111561245c57600080fd5b50612469868287016123c4565b9497909650939450505050565b6001600160801b03169052565b80516001600160a01b03908116835260208083015160a082860181905281519086018190526000939183019290849060c08801905b808310156124da578551851682529483019460019290920191908301906124b8565b5060408701516040890152606087015194506124f96060890186612476565b6080870151945061250d6080890186612476565b979650505050505050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b8281101561256f57603f1988860301845261255d858351612483565b94509285019290850190600101612541565b5092979650505050505050565b60006020828403121561258e57600080fd5b81356001600160401b038111156125a457600080fd5b8201601f810184136125b557600080fd5b611c198482356020840161215d565b600080600080606085870312156125da57600080fd5b84356125e5816120f2565b93506020850135925060408501356001600160401b0381111561260757600080fd5b612613878288016123c4565b95989497509550505050565b600082601f83011261263057600080fd5b813560206001600160401b0382111561264b5761264b612117565b8160051b61265a82820161212d565b928352848101820192828101908785111561267457600080fd5b83870192505b8483101561250d5782358252918301919083019061267a565b600080600080600060a086880312156126ab57600080fd5b85356126b6816120f2565b945060208601356126c6816120f2565b935060408601356001600160401b03808211156126e257600080fd5b6126ee89838a0161261f565b9450606088013591508082111561270457600080fd5b61271089838a0161261f565b9350608088013591508082111561272657600080fd5b50612733888289016121b4565b9150509295509295909350565b60005b8381101561275b578181015183820152602001612743565b50506000910152565b6000815180845261277c816020860160208601612740565b601f01601f19169290920160200192915050565b602081526000610e0b6020830184612764565b6020808252825182820181905260009190848201906040850190845b818110156127e45783516001600160a01b0316835292840192918401916001016127bf565b50909695505050505050565b602081526000610e0b6020830184612483565b600080600080600060a0868803121561281b57600080fd5b8535612826816120f2565b94506020860135612836816120f2565b9350604086013592506060860135915060808601356001600160401b0381111561285f57600080fd5b612733888289016121b4565b60006020828403121561287d57600080fd5b8151610e0b816120f2565b60208082526021908201527f4163636f756e743a206e6f742061646d696e206f7220456e747279506f696e746040820152601760f91b606082015260800190565b634e487b7160e01b600052603260045260246000fd5b6000808335601e198436030181126128f657600080fd5b8301803591506001600160401b0382111561291057600080fd5b602001915036819003821315611c8357600080fd5b6001600160a01b0391909116815260200190565b80356001600160801b038116811461211257600080fd5b60006020828403121561296257600080fd5b610e0b82612939565b803560ff8116811461211257600080fd5b60006020828403121561298e57600080fd5b610e0b8261296b565b634e487b7160e01b600052601160045260246000fd5b8082018082111561042f5761042f612997565b6000808335601e198436030181126129d757600080fd5b8301803591506001600160401b038211156129f157600080fd5b6020019150600581901b3603821315611c8357600080fd5b6000808335601e19843603018112612a2057600080fd5b83016020810192503590506001600160401b03811115612a3f57600080fd5b8060051b3603821315611c8357600080fd5b8183526000602080850194508260005b85811015612a8f578135612a74816120f2565b6001600160a01b031687529582019590820190600101612a61565b509495945050505050565b60208152612abb60208201612aae84612107565b6001600160a01b03169052565b6000612ac96020840161296b565b60ff8116604084015250612ae06040840184612a09565b610120806060860152612af861014086018385612a51565b925060608601356080860152612b1060808701612939565b9150612b1f60a0860183612476565b612b2b60a08701612939565b9150612b3a60c0860183612476565b612b4660c08701612939565b9150612b5560e0860183612476565b612b6160e08701612939565b9150610100612b7281870184612476565b9590950135939094019290925250919050565b600060018201612b9757612b97612997565b5060010190565b600181811c90821680612bb257607f821691505b602082108103612bd257634e487b7160e01b600052602260045260246000fd5b50919050565b600060208284031215612bea57600080fd5b81518015158114610e0b57600080fd5b60008251612c0c818460208701612740565b9190910192915050565b601f821115612c5e576000816000526020600020601f850160051c81016020861015612c3f5750805b601f850160051c820191505b8181101561099c57828155600101612c4b565b505050565b81516001600160401b03811115612c7c57612c7c612117565b612c9081612c8a8454612b9e565b84612c16565b602080601f831160018114612cc55760008415612cad5750858301515b600019600386901b1c1916600185901b17855561099c565b600085815260208120601f198616915b82811015612cf457888601518255948401946001909101908401612cd5565b5085821015612d125787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b604081526000612d356040830185612764565b8281036020840152612d478185612764565b95945050505050565b60008184825b85811015612d87578135612d69816120f2565b6001600160a01b031683526020928301929190910190600101612d56565b509095945050505050565b634e487b7160e01b600052602160045260246000fd5b8181038181111561042f5761042f612997565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220597237df4b3d5e31eb695964a10aa951c1d2e74f250fd8cbb7111b181659042f64736f6c63430008170033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.