Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
To
|
Amount
|
||
|---|---|---|---|---|---|---|---|
| 0x3d60ad80 | 6580892 | 509 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Minimal Proxy Contract for 0x36a5f0d61f6bab3c6dde211e5a6762cb18a8060d
Contract Name:
IPAccountImpl
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 20000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import { IERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import { IERC6551Account } from "erc6551/interfaces/IERC6551Account.sol";
import { IAccessController } from "./interfaces/access/IAccessController.sol";
import { IIPAccount } from "./interfaces/IIPAccount.sol";
import { MetaTx } from "./lib/MetaTx.sol";
import { Errors } from "./lib/Errors.sol";
import { IPAccountStorage } from "./IPAccountStorage.sol";
/// @title IPAccountImpl
/// @notice The Story Protocol's implementation of the IPAccount.
contract IPAccountImpl is IPAccountStorage, IIPAccount {
address public immutable ACCESS_CONTROLLER;
/// @notice Returns the IPAccount's internal nonce for transaction ordering.
uint256 public state;
receive() external payable override(IERC6551Account) {}
/// @notice Creates a new IPAccountImpl contract instance
/// @dev Initializes the IPAccountImpl with an AccessController address which is stored
/// in the implementation code's storage.
/// This means that each cloned IPAccount will inherently use the same AccessController
/// without the need for individual configuration.
/// @param accessController The address of the AccessController contract to be used for permission checks
constructor(
address accessController,
address ipAssetRegistry,
address licenseRegistry,
address moduleRegistry
) IPAccountStorage(ipAssetRegistry, licenseRegistry, moduleRegistry) {
if (accessController == address(0)) revert Errors.IPAccount__ZeroAccessController();
ACCESS_CONTROLLER = accessController;
}
/// @notice Checks if the contract supports a specific interface
/// @param interfaceId The interface identifier, as specified in ERC-165
/// @return bool is true if the contract supports the interface, false otherwise
function supportsInterface(bytes4 interfaceId) public view override(IPAccountStorage, IERC165) returns (bool) {
return (interfaceId == type(IIPAccount).interfaceId ||
interfaceId == type(IERC6551Account).interfaceId ||
interfaceId == type(IERC1155Receiver).interfaceId ||
interfaceId == type(IERC721Receiver).interfaceId ||
super.supportsInterface(interfaceId));
}
/// @notice Returns the identifier of the non-fungible token which owns the account
/// @return chainId The EIP-155 ID of the chain the token exists on
/// @return tokenContract The contract address of the token
/// @return tokenId The ID of the token
function token() public view override returns (uint256, address, uint256) {
bytes memory footer = new bytes(0x60);
// 0x4d = 77 bytes (ERC-1167 Header, address, ERC-1167 Footer, salt)
// 0x60 = 96 bytes (chainId, tokenContract, tokenId)
// ERC-1167 Header (10 bytes)
// <implementation (address)> (20 bytes)
// ERC-1167 Footer (15 bytes)
// <salt (uint256)> (32 bytes)
// <chainId (uint256)> (32 bytes)
// <tokenContract (address)> (32 bytes)
// <tokenId (uint256)> (32 bytes)
assembly {
extcodecopy(address(), add(footer, 0x20), 0x4d, 0x60)
}
return abi.decode(footer, (uint256, address, uint256));
}
/// @notice Checks if the signer is valid for the given data
/// @param signer The signer to check
/// @param data The data to check against
/// @return The function selector if the signer is valid, 0 otherwise
function isValidSigner(address signer, bytes calldata data) external view returns (bytes4) {
if (_isValidSigner(signer, address(0), data)) {
return IERC6551Account.isValidSigner.selector;
}
return bytes4(0);
}
/// @notice Returns the owner of the IP Account.
/// @return The address of the owner.
function owner() public view returns (address) {
(uint256 chainId, address contractAddress, uint256 tokenId) = token();
if (chainId != block.chainid) return address(0);
return IERC721(contractAddress).ownerOf(tokenId);
}
/// @dev Checks if the signer is valid for the given data and recipient via the AccessController permission system.
/// @param signer The signer to check
/// @param to The recipient of the transaction
/// @param data The calldata to check against
/// @return bool is true if the signer is valid, false otherwise
function _isValidSigner(address signer, address to, bytes calldata data) internal view returns (bool) {
if (data.length > 0 && data.length < 4) {
revert Errors.IPAccount__InvalidCalldata();
}
bytes4 selector = bytes4(0);
if (data.length >= 4) {
selector = bytes4(data[:4]);
}
// the check will revert if permission is denied
IAccessController(ACCESS_CONTROLLER).checkPermission(address(this), signer, to, selector);
return true;
}
/// @notice Executes a transaction from the IP Account on behalf of the signer.
/// @param to The recipient of the transaction.
/// @param value The amount of Ether to send.
/// @param data The data to send along with the transaction.
/// @param signer The signer of the transaction.
/// @param deadline The deadline of the transaction signature.
/// @param signature The signature of the transaction, EIP-712 encoded.
function executeWithSig(
address to,
uint256 value,
bytes calldata data,
address signer,
uint256 deadline,
bytes calldata signature
) external payable returns (bytes memory result) {
if (signer == address(0)) {
revert Errors.IPAccount__InvalidSigner();
}
if (deadline < block.timestamp) {
revert Errors.IPAccount__ExpiredSignature();
}
++state;
bytes32 digest = MessageHashUtils.toTypedDataHash(
MetaTx.calculateDomainSeparator(),
MetaTx.getExecuteStructHash(
MetaTx.Execute({ to: to, value: value, data: data, nonce: state, deadline: deadline })
)
);
if (!SignatureChecker.isValidSignatureNow(signer, digest, signature)) {
revert Errors.IPAccount__InvalidSignature();
}
result = _execute(signer, to, value, data);
emit ExecutedWithSig(to, value, data, state, deadline, signer, signature);
}
/// @notice Executes a transaction from the IP Account.
/// @param to The recipient of the transaction.
/// @param value The amount of Ether to send.
/// @param data The data to send along with the transaction.
/// @return result The return data from the transaction.
function execute(address to, uint256 value, bytes calldata data) external payable returns (bytes memory result) {
++state;
result = _execute(msg.sender, to, value, data);
emit Executed(to, value, data, state);
}
/// @inheritdoc IERC721Receiver
function onERC721Received(address, address, uint256, bytes memory) public pure returns (bytes4) {
return this.onERC721Received.selector;
}
/// @inheritdoc IERC1155Receiver
function onERC1155Received(address, address, uint256, uint256, bytes memory) public pure returns (bytes4) {
return this.onERC1155Received.selector;
}
/// @inheritdoc IERC1155Receiver
function onERC1155BatchReceived(
address,
address,
uint256[] memory,
uint256[] memory,
bytes memory
) public pure returns (bytes4) {
return this.onERC1155BatchReceived.selector;
}
/// @dev Executes a transaction from the IP Account.
function _execute(
address signer,
address to,
uint256 value,
bytes calldata data
) internal returns (bytes memory result) {
require(_isValidSigner(signer, to, data), "Invalid signer");
bool success;
(success, result) = to.call{ value: value }(data);
if (!success) {
assembly {
revert(add(result, 32), mload(result))
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./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);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @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 `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/IERC1155Receiver.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Interface that must be implemented by smart contracts in order to receive
* ERC-1155 token transfers.
*/
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 v5.0.0) (utils/cryptography/SignatureChecker.sol)
pragma solidity ^0.8.20;
import {ECDSA} from "./ECDSA.sol";
import {IERC1271} from "../../interfaces/IERC1271.sol";
/**
* @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
* signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
* Argent and Safe Wallet (previously Gnosis Safe).
*/
library SignatureChecker {
/**
* @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
* signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
(address recovered, ECDSA.RecoverError error, ) = ECDSA.tryRecover(hash, signature);
return
(error == ECDSA.RecoverError.NoError && recovered == signer) ||
isValidERC1271SignatureNow(signer, hash, signature);
}
/**
* @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
* against the signer smart contract using ERC1271.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidERC1271SignatureNow(
address signer,
bytes32 hash,
bytes memory signature
) internal view returns (bool) {
(bool success, bytes memory result) = signer.staticcall(
abi.encodeCall(IERC1271.isValidSignature, (hash, signature))
);
return (success &&
result.length >= 32 &&
abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @dev the ERC-165 identifier for this interface is `0x6faff5f1`
interface IERC6551Account {
/**
* @dev Allows the account to receive Ether.
*
* Accounts MUST implement a `receive` function.
*
* Accounts MAY perform arbitrary logic to restrict conditions
* under which Ether can be received.
*/
receive() external payable;
/**
* @dev Returns the identifier of the non-fungible token which owns the account.
*
* The return value of this function MUST be constant - it MUST NOT change over time.
*
* @return chainId The EIP-155 ID of the chain the token exists on
* @return tokenContract The contract address of the token
* @return tokenId The ID of the token
*/
function token()
external
view
returns (uint256 chainId, address tokenContract, uint256 tokenId);
/**
* @dev Returns a value that SHOULD be modified each time the account changes state.
*
* @return The current account state
*/
function state() external view returns (uint256);
/**
* @dev Returns a magic value indicating whether a given signer is authorized to act on behalf
* of the account.
*
* MUST return the bytes4 magic value 0x523e3260 if the given signer is valid.
*
* By default, the holder of the non-fungible token the account is bound to MUST be considered
* a valid signer.
*
* Accounts MAY implement additional authorization logic which invalidates the holder as a
* signer or grants signing permissions to other non-holder accounts.
*
* @param signer The address to check signing authorization for
* @param context Additional data used to determine whether the signer is valid
* @return magicValue Magic value indicating whether the signer is valid
*/
function isValidSigner(address signer, bytes calldata context)
external
view
returns (bytes4 magicValue);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;
import { AccessPermission } from "../../lib/AccessPermission.sol";
interface IAccessController {
/// @notice Emitted when a permission is set.
/// @param ipAccount The address of the IP account that grants the permission for `signer`
/// @param signer The address that can call `to` on behalf of the IP account
/// @param to The address that can be called by the `signer` (currently only modules can be `to`)
/// @param func The function selector of `to` that can be called by the `signer` on behalf of the `ipAccount`
/// @param permission The permission level
event PermissionSet(
address ipAccountOwner,
address indexed ipAccount,
address indexed signer,
address indexed to,
bytes4 func,
uint8 permission
);
/// @notice Sets a batch of permissions in a single transaction.
/// @dev This function allows setting multiple permissions at once. Pausable.
/// @param permissions An array of `Permission` structs, each representing the permission to be set.
function setBatchPermissions(AccessPermission.Permission[] memory permissions) external;
/// @notice Sets the permission for a specific function call
/// @dev Each policy is represented as a mapping from an IP account address to a signer address to a recipient
/// address to a function selector to a permission level. The permission level can be 0 (ABSTAIN), 1 (ALLOW), or
/// 2 (DENY).
/// @dev By default, all policies are set to 0 (ABSTAIN), which means that the permission is not set.
/// The owner of ipAccount by default has all permission.
/// address(0) => wildcard
/// bytes4(0) => wildcard
/// Specific permission overrides wildcard permission.
/// @param ipAccount The address of the IP account that grants the permission for `signer`
/// @param signer The address that can call `to` on behalf of the `ipAccount`
/// @param to The address that can be called by the `signer` (currently only modules can be `to`)
/// @param func The function selector of `to` that can be called by the `signer` on behalf of the `ipAccount`
/// @param permission The new permission level
function setPermission(address ipAccount, address signer, address to, bytes4 func, uint8 permission) external;
/// @notice Sets permission to a signer for all functions across all modules.
/// @param ipAccount The address of the IP account that grants the permission for `signer`.
/// @param signer The address of the signer receiving the permissions.
/// @param permission The new permission.
function setAllPermissions(address ipAccount, address signer, uint8 permission) external;
/// @notice Checks the permission level for a specific function call. Reverts if permission is not granted.
/// Otherwise, the function is a noop.
/// @dev This function checks the permission level for a specific function call.
/// If a specific permission is set, it overrides the general (wildcard) permission.
/// If the current level permission is ABSTAIN, the final permission is determined by the upper level.
/// @param ipAccount The address of the IP account that grants the permission for `signer`
/// @param signer The address that can call `to` on behalf of the `ipAccount`
/// @param to The address that can be called by the `signer` (currently only modules can be `to`)
/// @param func The function selector of `to` that can be called by the `signer` on behalf of the `ipAccount`
function checkPermission(address ipAccount, address signer, address to, bytes4 func) external view;
/// @notice Returns the permission level for a specific function call.
/// @param ipAccount The address of the IP account that grants the permission for `signer`
/// @param signer The address that can call `to` on behalf of the `ipAccount`
/// @param to The address that can be called by the `signer` (currently only modules can be `to`)
/// @param func The function selector of `to` that can be called by the `signer` on behalf of the `ipAccount`
/// @return permission The current permission level for the function call on `to` by the `signer` for `ipAccount`
function getPermission(address ipAccount, address signer, address to, bytes4 func) external view returns (uint8);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;
import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import { IERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import { IERC6551Account } from "erc6551/interfaces/IERC6551Account.sol";
import { IIPAccountStorage } from "./IIPAccountStorage.sol";
/// @title IIPAccount
/// @dev IPAccount is a token-bound account that adopts the EIP-6551 standard.
/// These accounts are deployed at deterministic addresses through the official 6551 account registry.
/// As a deployed smart contract, IPAccount can store IP-related information,
/// like ownership of other NFTs such as license NFT or Royalty NFT.
/// IPAccount can interact with modules by making calls as a normal transaction sender.
/// This allows for seamless operations on the state and data of IP.
/// IPAccount is core identity for all actions.
interface IIPAccount is IERC6551Account, IERC721Receiver, IERC1155Receiver, IIPAccountStorage {
/// @notice Emitted when a transaction is executed.
/// @param to The recipient of the transaction.
/// @param value The amount of Ether sent.
/// @param data The data sent along with the transaction.
/// @param nonce The nonce of the transaction.
event Executed(address indexed to, uint256 value, bytes data, uint256 nonce);
/// @notice Emitted when a transaction is executed on behalf of the signer.
/// @param to The recipient of the transaction.
/// @param value The amount of Ether sent.
/// @param data The data sent along with the transaction.
/// @param nonce The nonce of the transaction.
/// @param deadline The deadline of the transaction signature.
/// @param signer The signer of the transaction.
/// @param signature The signature of the transaction, EIP-712 encoded.
event ExecutedWithSig(
address indexed to,
uint256 value,
bytes data,
uint256 nonce,
uint256 deadline,
address indexed signer,
bytes signature
);
/// @notice Returns the IPAccount's internal nonce for transaction ordering.
function state() external view returns (uint256);
/// @notice Returns the identifier of the non-fungible token which owns the account
/// @return chainId The EIP-155 ID of the chain the token exists on
/// @return tokenContract The contract address of the token
/// @return tokenId The ID of the token
function token() external view returns (uint256, address, uint256);
/// @notice Checks if the signer is valid for the given data
/// @param signer The signer to check
/// @param data The data to check against
/// @return The function selector if the signer is valid, 0 otherwise
function isValidSigner(address signer, bytes calldata data) external view returns (bytes4);
/// @notice Returns the owner of the IP Account.
/// @return owner The address of the owner.
function owner() external view returns (address);
/// @notice Executes a transaction from the IP Account on behalf of the signer.
/// @param to The recipient of the transaction.
/// @param value The amount of Ether to send.
/// @param data The data to send along with the transaction.
/// @param signer The signer of the transaction.
/// @param deadline The deadline of the transaction signature.
/// @param signature The signature of the transaction, EIP-712 encoded.
/// @return result The return data from the transaction.
function executeWithSig(
address to,
uint256 value,
bytes calldata data,
address signer,
uint256 deadline,
bytes calldata signature
) external payable returns (bytes memory);
/// @notice Executes a transaction from the IP Account.
/// @param to The recipient of the transaction.
/// @param value The amount of Ether to send.
/// @param data The data to send along with the transaction.
/// @return result The return data from the transaction.
function execute(address to, uint256 value, bytes calldata data) external payable returns (bytes memory);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;
/// @title MetaTx
/// @dev This library provides functions for handling meta transactions in the Story Protocol.
library MetaTx {
/// @dev Version of the EIP712 domain.
string public constant EIP712_DOMAIN_VERSION = "1";
/// @dev Hash of the EIP712 domain version.
bytes32 public constant EIP712_DOMAIN_VERSION_HASH = keccak256(bytes(EIP712_DOMAIN_VERSION));
/// @dev EIP712 domain type hash.
bytes32 public constant EIP712_DOMAIN =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
/// @dev Execute type hash.
bytes32 public constant EXECUTE =
keccak256("Execute(address to,uint256 value,bytes data,uint256 nonce,uint256 deadline)");
/// @dev Structure for the Execute type.
struct Execute {
address to;
uint256 value;
bytes data;
uint256 nonce;
uint256 deadline;
}
/// @dev Calculates the EIP712 domain separator for the current contract.
/// @return The EIP712 domain separator.
function calculateDomainSeparator() internal view returns (bytes32) {
return calculateDomainSeparator(address(this));
}
/// @dev Calculates the EIP712 domain separator for a given IP account.
/// @param ipAccount The IP account for which to calculate the domain separator.
/// @return The EIP712 domain separator.
function calculateDomainSeparator(address ipAccount) internal view returns (bytes32) {
return
keccak256(
abi.encode(
EIP712_DOMAIN,
keccak256("Story Protocol IP Account"),
EIP712_DOMAIN_VERSION_HASH,
block.chainid,
ipAccount
)
);
}
/// @dev Calculates the EIP712 struct hash of an Execute.
/// @param execute The Execute to hash.
/// @return The EIP712 struct hash of the Execute.
function getExecuteStructHash(Execute memory execute) internal pure returns (bytes32) {
return
keccak256(
abi.encode(
MetaTx.EXECUTE,
execute.to,
execute.value,
keccak256(execute.data),
execute.nonce,
execute.deadline
)
);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;
/// @title Errors Library
/// @notice Library for all Story Protocol contract errors.
library Errors {
////////////////////////////////////////////////////////////////////////////
// IP Account //
////////////////////////////////////////////////////////////////////////////
/// @notice Zero address provided for Access Controller.
error IPAccount__ZeroAccessController();
/// @notice Invalid signer provided.
error IPAccount__InvalidSigner();
/// @notice Invalid signature provided, must be an EIP-712 signature.
error IPAccount__InvalidSignature();
/// @notice Signature is expired.
error IPAccount__ExpiredSignature();
/// @notice Provided calldata is invalid.
error IPAccount__InvalidCalldata();
////////////////////////////////////////////////////////////////////////////
// IP Account Storage //
////////////////////////////////////////////////////////////////////////////
/// @notice Caller writing to IP Account storage is not a registered module.
error IPAccountStorage__NotRegisteredModule(address module);
////////////////////////////////////////////////////////////////////////////
// IP Account Registry //
////////////////////////////////////////////////////////////////////////////
/// @notice Zero address provided for IP Account implementation.
error IPAccountRegistry_ZeroIpAccountImpl();
////////////////////////////////////////////////////////////////////////////
// IP Asset Registry //
////////////////////////////////////////////////////////////////////////////
/// @notice Zero address provided for Access Manager in initializer.
error IPAssetRegistry__ZeroAccessManager();
/// @notice The IP asset has already been registered.
error IPAssetRegistry__AlreadyRegistered();
/// @notice The NFT token contract is not valid ERC721 contract.
error IPAssetRegistry__UnsupportedIERC721(address contractAddress);
/// @notice The NFT token contract does not support ERC721Metadata.
error IPAssetRegistry__UnsupportedIERC721Metadata(address contractAddress);
/// @notice The NFT token id does not exist or invalid.
error IPAssetRegistry__InvalidToken(address contractAddress, uint256 tokenId);
////////////////////////////////////////////////////////////////////////////
// License Registry //
////////////////////////////////////////////////////////////////////////////
/// @notice Zero address provided for Access Manager in initializer.
error LicenseRegistry__ZeroAccessManager();
/// @notice Zero address provided for IP Asset Registry.
error LicensingModule__ZeroRoyaltyModule();
/// @notice Zero address provided for Licensing Module.
error LicensingModule__ZeroLicenseRegistry();
/// @notice Zero address provided for Dispute Module.
error LicensingModule__ZeroDisputeModule();
/// @notice Zero address provided for License Token.
error LicensingModule__ZeroLicenseToken();
/// @notice Zero address provided for Module Registry.
error LicensingModule__ZeroModuleRegistry();
/// @notice Zero address provided for Licensing Module.
error LicenseRegistry__ZeroLicensingModule();
/// @notice Zero address provided for Dispute Module.
error LicenseRegistry__ZeroDisputeModule();
/// @notice Caller is not the Licensing Module.
error LicenseRegistry__CallerNotLicensingModule();
/// @notice Emitted when trying to transfer a license that is not transferable (by policy)
error LicenseRegistry__NotTransferable();
/// @notice License Template is not registered in the License Registry.
error LicenseRegistry__UnregisteredLicenseTemplate(address licenseTemplate);
/// @notice License Terms or License Template not found.
error LicenseRegistry__LicenseTermsNotExists(address licenseTemplate, uint256 licenseTermsId);
/// @notice Licensor IP does not have the provided license terms attached.
error LicenseRegistry__LicensorIpHasNoLicenseTerms(address ipId, address licenseTemplate, uint256 licenseTermsId);
/// @notice Invalid License Template address provided.
error LicenseRegistry__NotLicenseTemplate(address licenseTemplate);
/// @notice IP is expired.
error LicenseRegistry__IpExpired(address ipId);
/// @notice Parent IP is expired.
error LicenseRegistry__ParentIpExpired(address ipId);
/// @notice Parent IP is dispute tagged.
error LicenseRegistry__ParentIpTagged(address ipId);
/// @notice Parent IP does not have the provided license terms attached.
error LicenseRegistry__ParentIpHasNoLicenseTerms(address ipId, uint256 licenseTermsId);
/// @notice Empty Parent IP list provided.
error LicenseRegistry__NoParentIp();
/// @notice Provided derivative IP already has license terms attached.
error LicenseRegistry__DerivativeIpAlreadyHasLicense(address childIpId);
/// @notice Provided derivative IP is already registered.
error LicenseRegistry__DerivativeAlreadyRegistered(address childIpId);
/// @notice Provided derivative IP is the same as the parent IP.
error LicenseRegistry__DerivativeIsParent(address ipId);
/// @notice Provided license template does not match the parent IP's current license template.
error LicenseRegistry__ParentIpUnmatchedLicenseTemplate(address ipId, address licenseTemplate);
/// @notice Index out of bounds.
error LicenseRegistry__IndexOutOfBounds(address ipId, uint256 index, uint256 length);
/// @notice Provided license template and terms ID is already attached to IP.
error LicenseRegistry__LicenseTermsAlreadyAttached(address ipId, address licenseTemplate, uint256 licenseTermsId);
/// @notice Provided license template does not match the IP's current license template.
error LicenseRegistry__UnmatchedLicenseTemplate(address ipId, address licenseTemplate, address newLicenseTemplate);
/// @notice Provided license template and terms ID is a duplicate.
error LicenseRegistry__DuplicateLicense(address ipId, address licenseTemplate, uint256 licenseTermsId);
////////////////////////////////////////////////////////////////////////////
// License Token //
////////////////////////////////////////////////////////////////////////////
/// @notice Zero address provided for Access Manager in initializer.
error LicenseToken__ZeroAccessManager();
/// @notice Zero address provided for Licensing Module.
error LicenseToken__ZeroLicensingModule();
/// @notice Zero address provided for Dispute Module.
error LicenseToken__ZeroDisputeModule();
/// @notice Caller is not the Licensing Module.
error LicenseToken__CallerNotLicensingModule();
/// @notice License token is revoked.
error LicenseToken__RevokedLicense(uint256 tokenId);
/// @notice License token is not transferable.
error LicenseToken__NotTransferable();
/// @notice License token is not owned by the caller.
error LicenseToken__NotLicenseTokenOwner(uint256 tokenId, address iPowner, address tokenOwner);
/// @notice All license tokens must be from the same license template.
error LicenseToken__AllLicenseTokensMustFromSameLicenseTemplate(
address licenseTemplate,
address anotherLicenseTemplate
);
////////////////////////////////////////////////////////////////////////////
// Licensing Module //
////////////////////////////////////////////////////////////////////////////
/// @notice Zero address provided for Access Manager in initializer.
error LicensingModule__ZeroAccessManager();
/// @notice Receiver is zero address.
error LicensingModule__ReceiverZeroAddress();
/// @notice Mint amount is zero.
error LicensingModule__MintAmountZero();
/// @notice IP is dispute tagged.
error LicensingModule__DisputedIpId();
/// @notice License template and terms ID is not found.
error LicensingModule__LicenseTermsNotFound(address licenseTemplate, uint256 licenseTermsId);
/// @notice Derivative IP cannot add license terms.
error LicensingModule__DerivativesCannotAddLicenseTerms();
/// @notice Receiver check failed.
error LicensingModule__ReceiverCheckFailed(address receiver);
/// @notice IP list and license terms list length mismatch.
error LicensingModule__LicenseTermsLengthMismatch(uint256 ipLength, uint256 licenseTermsLength);
/// @notice Parent IP list is empty.
error LicensingModule__NoParentIp();
/// @notice Incompatible royalty policy.
error LicensingModule__IncompatibleRoyaltyPolicy(address royaltyPolicy, address anotherRoyaltyPolicy);
/// @notice License template and terms are not compatible for the derivative IP.
error LicensingModule__LicenseNotCompatibleForDerivative(address childIpId);
/// @notice License token list is empty.
error LicensingModule__NoLicenseToken();
/// @notice License tokens are not compatible for the derivative IP.
error LicensingModule__LicenseTokenNotCompatibleForDerivative(address childIpId, uint256[] licenseTokenIds);
/// @notice License template denied minting license token during the verification stage.
error LicensingModule__LicenseDenyMintLicenseToken(
address licenseTemplate,
uint256 licenseTermsId,
address licensorIpId
);
/// @notice Licensing hook is invalid either not support ILicensingHook interface or not registered as module
error LicensingModule__InvalidLicensingHook(address hook);
/// @notice The license terms ID is invalid or license template doesn't exist.
error LicensingModule__InvalidLicenseTermsId(address licenseTemplate, uint256 licenseTermsId);
////////////////////////////////////////////////////////////////////////////
// Dispute Module //
////////////////////////////////////////////////////////////////////////////
/// @notice Zero address provided for Access Manager in initializer.
error DisputeModule__ZeroAccessManager();
/// @notice Zero address provided for License Registry.
error DisputeModule__ZeroLicenseRegistry();
/// @notice Zero address provided for IP Asset Registry.
error DisputeModule__ZeroIPAssetRegistry();
/// @notice Zero address provided for Access Controller.
error DisputeModule__ZeroAccessController();
/// @notice Zero address provided for Arbitration Policy.
error DisputeModule__ZeroArbitrationPolicy();
/// @notice Zero address provided for Arbitration Relayer.
error DisputeModule__ZeroArbitrationRelayer();
/// @notice Zero bytes provided for Dispute Tag.
error DisputeModule__ZeroDisputeTag();
/// @notice Zero bytes provided for Dispute Evidence.
error DisputeModule__ZeroLinkToDisputeEvidence();
/// @notice Not a whitelisted arbitration policy.
error DisputeModule__NotWhitelistedArbitrationPolicy();
/// @notice Not a whitelisted arbitration relayer.
error DisputeModule__NotWhitelistedArbitrationRelayer();
/// @notice Not a whitelisted dispute tag.
error DisputeModule__NotWhitelistedDisputeTag();
/// @notice Not the dispute initiator.
error DisputeModule__NotDisputeInitiator();
/// @notice Not in dispute state, the dispute is not IN_DISPUTE.
error DisputeModule__NotInDisputeState();
/// @notice Not able to resolve a dispute, either the dispute is IN_DISPUTE or empty.
error DisputeModule__NotAbleToResolve();
/// @notice Not a registered IP.
error DisputeModule__NotRegisteredIpId();
/// @notice Provided parent IP and the parent dispute's target IP is different.
error DisputeModule__ParentIpIdMismatch();
/// @notice Provided parent dispute's target IP is not dispute tagged.
error DisputeModule__ParentNotTagged();
/// @notice Provided parent dispute's target IP is not the derivative IP's parent.
error DisputeModule__NotDerivative();
/// @notice Provided parent dispute has not been resolved.
error DisputeModule__ParentDisputeNotResolved();
////////////////////////////////////////////////////////////////////////////
// ArbitrationPolicy SP //
////////////////////////////////////////////////////////////////////////////
/// @notice Zero address provided for Access Manager in initializer.
error ArbitrationPolicySP__ZeroAccessManager();
/// @notice Zero address provided for Dispute Module.
error ArbitrationPolicySP__ZeroDisputeModule();
/// @notice Zero address provided for Payment Token.
error ArbitrationPolicySP__ZeroPaymentToken();
/// @notice Caller is not the Dispute Module.
error ArbitrationPolicySP__NotDisputeModule();
////////////////////////////////////////////////////////////////////////////
// Royalty Module //
////////////////////////////////////////////////////////////////////////////
/// @notice Zero address provided for Access Manager in initializer.
error RoyaltyModule__ZeroAccessManager();
/// @notice Zero address provided for Dispute Module.
error RoyaltyModule__ZeroDisputeModule();
/// @notice Zero address provided for License Registry.
error RoyaltyModule__ZeroLicenseRegistry();
/// @notice Zero address provided for Licensing Module.
error RoyaltyModule__ZeroLicensingModule();
/// @notice Zero address provided for Royalty Policy.
error RoyaltyModule__ZeroRoyaltyPolicy();
/// @notice Zero address provided for Royalty Token.
error RoyaltyModule__ZeroRoyaltyToken();
/// @notice Not a whitelisted royalty policy.
error RoyaltyModule__NotWhitelistedRoyaltyPolicy();
/// @notice Not a whitelisted royalty token.
error RoyaltyModule__NotWhitelistedRoyaltyToken();
/// @notice Royalty policy for IP is unset.
error RoyaltyModule__NoRoyaltyPolicySet();
/// @notice Royalty policy between IPs are incompatible (different).
error RoyaltyModule__IncompatibleRoyaltyPolicy();
/// @notice Caller is unauthorized.
error RoyaltyModule__NotAllowedCaller();
/// @notice IP can only mint licenses of selected royalty policy.
error RoyaltyModule__CanOnlyMintSelectedPolicy();
/// @notice Parent IP list for linking is empty.
error RoyaltyModule__NoParentsOnLinking();
/// @notice IP is expired.
error RoyaltyModule__IpIsExpired();
/// @notice IP is dipute tagged.
error RoyaltyModule__IpIsTagged();
////////////////////////////////////////////////////////////////////////////
// Royalty Policy LAP //
////////////////////////////////////////////////////////////////////////////
/// @notice Zero address provided for Access Manager in initializer.
error RoyaltyPolicyLAP__ZeroAccessManager();
/// @notice Zero address provided for IP Royalty Vault Beacon.
error RoyaltyPolicyLAP__ZeroIpRoyaltyVaultBeacon();
/// @notice Zero address provided for Royalty Module.
error RoyaltyPolicyLAP__ZeroRoyaltyModule();
/// @notice Zero address provided for Licensing Module.
error RoyaltyPolicyLAP__ZeroLicensingModule();
/// @notice Caller is not the Royalty Module.
error RoyaltyPolicyLAP__NotRoyaltyModule();
/// @notice Size of parent IP list is above the LAP royalty policy limit.
error RoyaltyPolicyLAP__AboveParentLimit();
/// @notice Amount of ancestors for derivative IP is above the LAP royalty policy limit.
error RoyaltyPolicyLAP__AboveAncestorsLimit();
/// @notice Total royalty stack exceeds the protocol limit.
error RoyaltyPolicyLAP__AboveRoyaltyStackLimit();
/// @notice Size of parent royalties list and parent IP list mismatch.
error RoyaltyPolicyLAP__InvalidParentRoyaltiesLength();
/// @notice IP cannot be linked to a parent, because it is either already linked to parents or derivatives (root).
error RoyaltyPolicyLAP__UnlinkableToParents();
/// @notice Policy is already initialized and IP is at the ancestors limit, so it can't mint more licenses.
error RoyaltyPolicyLAP__LastPositionNotAbleToMintLicense();
////////////////////////////////////////////////////////////////////////////
// IP Royalty Vault //
////////////////////////////////////////////////////////////////////////////
/// @notice Zero address provided for Royalty Policy LAP.
error IpRoyaltyVault__ZeroRoyaltyPolicyLAP();
/// @notice Zero address provided for Dispute Module.
error IpRoyaltyVault__ZeroDisputeModule();
/// @notice Caller is not the Royalty Policy LAP.
error IpRoyaltyVault__NotRoyaltyPolicyLAP();
/// @notice Snapshot interval is too short, wait for the interval to pass for the next snapshot.
error IpRoyaltyVault__SnapshotIntervalTooShort();
/// @notice Royalty Tokens is already claimed.
error IpRoyaltyVault__AlreadyClaimed();
/// @notice Royalty Tokens claimer is not an ancestor of derivative IP.
error IpRoyaltyVault__ClaimerNotAnAncestor();
/// @notice IP is dispute tagged.
error IpRoyaltyVault__IpTagged();
/// @notice IP Royalty Vault is paused.
error IpRoyaltyVault__EnforcedPause();
////////////////////////////////////////////////////////////////////////////
// Module Registry //
////////////////////////////////////////////////////////////////////////////
/// @notice Zero address provided for Access Manager in initializer.
error ModuleRegistry__ZeroAccessManager();
/// @notice Module is zero address.
error ModuleRegistry__ModuleAddressZeroAddress();
/// @notice Provided module address is not a contract.
error ModuleRegistry__ModuleAddressNotContract();
/// @notice Module is already registered.
error ModuleRegistry__ModuleAlreadyRegistered();
/// @notice Provided module name is empty string.
error ModuleRegistry__NameEmptyString();
/// @notice Provided module name is already regsitered.
error ModuleRegistry__NameAlreadyRegistered();
/// @notice Module name does not match the given name.
error ModuleRegistry__NameDoesNotMatch();
/// @notice Module is not registered
error ModuleRegistry__ModuleNotRegistered();
/// @notice Provided interface ID is zero bytes4.
error ModuleRegistry__InterfaceIdZero();
/// @notice Module type is already registered.
error ModuleRegistry__ModuleTypeAlreadyRegistered();
/// @notice Module type is not registered.
error ModuleRegistry__ModuleTypeNotRegistered();
/// @notice Module address does not support the interface ID (module type).
error ModuleRegistry__ModuleNotSupportExpectedModuleTypeInterfaceId();
/// @notice Module type is empty string.
error ModuleRegistry__ModuleTypeEmptyString();
////////////////////////////////////////////////////////////////////////////
// Access Controller //
////////////////////////////////////////////////////////////////////////////
/// @notice Zero address provided for Access Manager in initializer.
error AccessController__ZeroAccessManager();
/// @notice Zero address provided for IP Account Registry.
error AccessController__ZeroIPAccountRegistry();
/// @notice Zero address provided for Module Registry.
error AccessController__ZeroModuleRegistry();
/// @notice IP Account is zero address.
error AccessController__IPAccountIsZeroAddress();
/// @notice IP Account is not a valid SP IP Account address.
error AccessController__IPAccountIsNotValid(address ipAccount);
/// @notice Signer is zero address.
error AccessController__SignerIsZeroAddress();
/// @notice Caller is not the IP Account or its owner.
error AccessController__CallerIsNotIPAccountOrOwner();
/// @notice Invalid permission value, must be 0 (ABSTAIN), 1 (ALLOW) or 2 (DENY).
error AccessController__PermissionIsNotValid();
/// @notice Both the caller and recipient (to) are not registered modules.
error AccessController__BothCallerAndRecipientAreNotRegisteredModule(address signer, address to);
/// @notice Permission denied.
error AccessController__PermissionDenied(address ipAccount, address signer, address to, bytes4 func);
/// @notice Both recipient (to) and function selectors are zero address means delegate all permissions to signer.
error AccessController__ToAndFuncAreZeroAddressShouldCallSetAllPermissions();
////////////////////////////////////////////////////////////////////////////
// Access Controlled //
////////////////////////////////////////////////////////////////////////////
/// @notice Zero address passed.
error AccessControlled__ZeroAddress();
/// @notice IP Account is not a valid SP IP Account address.
error AccessControlled__NotIpAccount(address ipAccount);
/// @notice Caller is not the IP Account.
error AccessControlled__CallerIsNotIpAccount(address caller);
////////////////////////////////////////////////////////////////////////////
// Core Metadata Module //
////////////////////////////////////////////////////////////////////////////
/// @notice Core metadata is already frozen (immutable).
error CoreMetadataModule__MetadataAlreadyFrozen();
////////////////////////////////////////////////////////////////////////////
// Protocol Pause Admin //
////////////////////////////////////////////////////////////////////////////
/// @notice Zero address passed.
error ProtocolPauseAdmin__ZeroAddress();
/// @notice Adding a contract that is paused.
error ProtocolPauseAdmin__AddingPausedContract();
/// @notice Contract is already added to the pausable list.
error ProtocolPauseAdmin__PausableAlreadyAdded();
/// @notice Removing a contract that is not in the pausable list.
error ProtocolPauseAdmin__PausableNotFound();
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;
import { IIPAccountStorage } from "./interfaces/IIPAccountStorage.sol";
import { IModuleRegistry } from "./interfaces/registries/IModuleRegistry.sol";
import { Errors } from "./lib/Errors.sol";
import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortStrings.sol";
/// @title IPAccount Storage
/// @dev Implements the IIPAccountStorage interface for managing IPAccount's state using a namespaced storage pattern.
/// Inherits all functionalities from IIPAccountStorage, providing concrete implementations for the interface's methods.
/// This contract allows Modules to store and retrieve data in a structured and conflict-free manner
/// by utilizing namespaces, where the default namespace is determined by the
/// `msg.sender` (the caller Module's address).
contract IPAccountStorage is ERC165, IIPAccountStorage {
using ShortStrings for *;
address public immutable MODULE_REGISTRY;
address public immutable LICENSE_REGISTRY;
address public immutable IP_ASSET_REGISTRY;
mapping(bytes32 => mapping(bytes32 => bytes)) public bytesData;
mapping(bytes32 => mapping(bytes32 => bytes32)) public bytes32Data;
modifier onlyRegisteredModule() {
if (
msg.sender != IP_ASSET_REGISTRY &&
msg.sender != LICENSE_REGISTRY &&
!IModuleRegistry(MODULE_REGISTRY).isRegistered(msg.sender)
) {
revert Errors.IPAccountStorage__NotRegisteredModule(msg.sender);
}
_;
}
constructor(address ipAssetRegistry, address licenseRegistry, address moduleRegistry) {
MODULE_REGISTRY = moduleRegistry;
LICENSE_REGISTRY = licenseRegistry;
IP_ASSET_REGISTRY = ipAssetRegistry;
}
/// @inheritdoc IIPAccountStorage
function setBytes(bytes32 key, bytes calldata value) external onlyRegisteredModule {
bytesData[_toBytes32(msg.sender)][key] = value;
}
/// @inheritdoc IIPAccountStorage
function getBytes(bytes32 key) external view returns (bytes memory) {
return bytesData[_toBytes32(msg.sender)][key];
}
/// @inheritdoc IIPAccountStorage
function getBytes(bytes32 namespace, bytes32 key) external view returns (bytes memory) {
return bytesData[namespace][key];
}
/// @inheritdoc IIPAccountStorage
function setBytes32(bytes32 key, bytes32 value) external onlyRegisteredModule {
bytes32Data[_toBytes32(msg.sender)][key] = value;
}
/// @inheritdoc IIPAccountStorage
function getBytes32(bytes32 key) external view returns (bytes32) {
return bytes32Data[_toBytes32(msg.sender)][key];
}
/// @inheritdoc IIPAccountStorage
function getBytes32(bytes32 namespace, bytes32 key) external view returns (bytes32) {
return bytes32Data[namespace][key];
}
/// @notice ERC165 interface identifier for IIPAccountStorage
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165) returns (bool) {
return interfaceId == type(IIPAccountStorage).interfaceId || super.supportsInterface(interfaceId);
}
function _toBytes32(string memory s) internal pure returns (bytes32) {
return ShortString.unwrap(s.toShortString());
}
function _toBytes32(address a) internal pure returns (bytes32) {
return bytes32(uint256(uint160(a)));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @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
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile 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 {MessageHashUtils-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]
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
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, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile 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 {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
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]
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
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.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
// 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, s);
}
// 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, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @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, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*/
interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
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_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;
/// @title Access Permission Library
/// @notice Library for IPAccount access control permissions.
/// These permissions are used by the AccessController.
library AccessPermission {
/// @notice ABSTAIN means having not enough information to make decision at current level, deferred decision to up
/// level permission.
uint8 public constant ABSTAIN = 0;
/// @notice ALLOW means the permission is granted to transaction signer to call the function.
uint8 public constant ALLOW = 1;
/// @notice DENY means the permission is denied to transaction signer to call the function.
uint8 public constant DENY = 2;
/// @notice This struct is used to represent permissions in batch operations within the AccessController.
/// @param ipAccount The address of the IP account that grants the permission for `signer`
/// @param signer The address that can call `to` on behalf of the `ipAccount`
/// @param to The address that can be called by the `signer` (currently only modules can be `to`)
/// @param func The function selector of `to` that can be called by the `signer` on behalf of the `ipAccount`
/// @param permission The permission level for the transaction (0 = ABSTAIN, 1 = ALLOW, 2 = DENY).
struct Permission {
address ipAccount;
address signer;
address to;
bytes4 func;
uint8 permission;
}
}// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.23;
/// @title IPAccount Namespaced Storage Interface
/// @dev Provides a structured way to store IPAccount's state using a namespaced storage pattern.
/// This interface facilitates conflict-free data writing by different Modules into the same IPAccount
/// by utilizing namespaces.
/// The default namespace for write operations is determined by the `msg.sender`, ensuring that only the owning Module
/// (i.e., the Module calling the write functions) can write data into its respective namespace.
/// However, read operations are unrestricted and can access any namespace.
///
/// Rules:
/// - The default namespace for a Module is its own address.
/// - Every Module can read data from any namespace.
/// - Only the owning Module (i.e., the Module whose address is used as the namespace) can write data into
/// its respective namespace.
interface IIPAccountStorage {
/// @dev Sets a bytes value under a given key within the default namespace, determined by `msg.sender`.
/// @param key The key under which to store the value.
/// @param value The bytes value to be stored.
function setBytes(bytes32 key, bytes calldata value) external;
/// @dev Retrieves a bytes value by a given key from the default namespace.
/// @param key The key whose value is to be retrieved.
/// @return The bytes value stored under the specified key.
function getBytes(bytes32 key) external view returns (bytes memory);
/// @dev Retrieves a bytes value by a given key from a specified namespace.
/// @param namespace The namespace from which to retrieve the value.
/// @param key The key whose value is to be retrieved.
/// @return The bytes value stored under the specified key in the given namespace.
function getBytes(bytes32 namespace, bytes32 key) external view returns (bytes memory);
/// @dev Sets a bytes32 value under a given key within the default namespace, determined by `msg.sender`.
/// @param key The key under which to store the value.
/// @param value The bytes32 value to be stored.
function setBytes32(bytes32 key, bytes32 value) external;
/// @dev Retrieves a bytes32 value by a given key from the default namespace.
/// @param key The key whose value is to be retrieved.
/// @return The bytes32 value stored under the specified key.
function getBytes32(bytes32 key) external view returns (bytes32);
/// @dev Retrieves a bytes32 value by a given key from a specified namespace.
/// @param namespace The namespace from which to retrieve the value.
/// @param key The key whose value is to be retrieved.
/// @return The bytes32 value stored under the specified key in the given namespace.
function getBytes32(bytes32 namespace, bytes32 key) external view returns (bytes32);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;
/// @title IModuleRegistry
/// @dev This interface defines the methods for a module registry in the Story Protocol.
interface IModuleRegistry {
/// @notice Emitted when a new module is added to the registry.
/// @param name The name of the module.
/// @param module The address of the module.
event ModuleAdded(string name, address indexed module, bytes4 indexed moduleTypeInterfaceId, string moduleType);
/// @notice Emitted when a module is removed from the registry.
/// @param name The name of the module.
/// @param module The address of the module.
event ModuleRemoved(string name, address indexed module);
/// @notice Returns the address of a registered module by its name.
/// @param name The name of the module.
/// @return moduleAddress The address of the module.
function getModule(string memory name) external view returns (address);
/// @notice Returns the module type of a registered module by its address.
/// @param moduleAddress The address of the module.
/// @return moduleType The type of the module as a string.
function getModuleType(address moduleAddress) external view returns (string memory);
/// @notice Returns the interface ID of a registered module type.
/// @param moduleType The name of the module type.
/// @return moduleTypeInterfaceId The interface ID of the module type as bytes4.
function getModuleTypeInterfaceId(string memory moduleType) external view returns (bytes4);
/// @notice Registers a new module type in the registry associate with an interface.
/// @dev Enforced to be only callable by the protocol admin in governance.
/// @param name The name of the module type to be registered.
/// @param interfaceId The interface ID associated with the module type.
function registerModuleType(string memory name, bytes4 interfaceId) external;
/// @notice Removes a module type from the registry.
/// @dev Enforced to be only callable by the protocol admin in governance.
/// @param name The name of the module type to be removed.
function removeModuleType(string memory name) external;
/// @notice Registers a new module in the registry.
/// @dev Enforced to be only callable by the protocol admin in governance.
/// @param name The name of the module.
/// @param moduleAddress The address of the module.
function registerModule(string memory name, address moduleAddress) external;
/// @notice Registers a new module in the registry with an associated module type.
/// @param name The name of the module to be registered.
/// @param moduleAddress The address of the module.
/// @param moduleType The type of the module being registered.
function registerModule(string memory name, address moduleAddress, string memory moduleType) external;
/// @notice Removes a module from the registry.
/// @dev Enforced to be only callable by the protocol admin in governance.
/// @param name The name of the module.
function removeModule(string memory name) external;
/// @notice Checks if a module is registered in the protocol.
/// @param moduleAddress The address of the module.
/// @return isRegistered True if the module is registered, false otherwise.
function isRegistered(address moduleAddress) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ShortStrings.sol)
pragma solidity ^0.8.20;
import {StorageSlot} from "./StorageSlot.sol";
// | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
// | length | 0x BB |
type ShortString is bytes32;
/**
* @dev This library provides functions to convert short memory strings
* into a `ShortString` type that can be used as an immutable variable.
*
* Strings of arbitrary length can be optimized using this library if
* they are short enough (up to 31 bytes) by packing them with their
* length (1 byte) in a single EVM word (32 bytes). Additionally, a
* fallback mechanism can be used for every other case.
*
* Usage example:
*
* ```solidity
* contract Named {
* using ShortStrings for *;
*
* ShortString private immutable _name;
* string private _nameFallback;
*
* constructor(string memory contractName) {
* _name = contractName.toShortStringWithFallback(_nameFallback);
* }
*
* function name() external view returns (string memory) {
* return _name.toStringWithFallback(_nameFallback);
* }
* }
* ```
*/
library ShortStrings {
// Used as an identifier for strings longer than 31 bytes.
bytes32 private constant FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
error StringTooLong(string str);
error InvalidShortString();
/**
* @dev Encode a string of at most 31 chars into a `ShortString`.
*
* This will trigger a `StringTooLong` error is the input string is too long.
*/
function toShortString(string memory str) internal pure returns (ShortString) {
bytes memory bstr = bytes(str);
if (bstr.length > 31) {
revert StringTooLong(str);
}
return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
}
/**
* @dev Decode a `ShortString` back to a "normal" string.
*/
function toString(ShortString sstr) internal pure returns (string memory) {
uint256 len = byteLength(sstr);
// using `new string(len)` would work locally but is not memory safe.
string memory str = new string(32);
/// @solidity memory-safe-assembly
assembly {
mstore(str, len)
mstore(add(str, 0x20), sstr)
}
return str;
}
/**
* @dev Return the length of a `ShortString`.
*/
function byteLength(ShortString sstr) internal pure returns (uint256) {
uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
if (result > 31) {
revert InvalidShortString();
}
return result;
}
/**
* @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
*/
function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
if (bytes(value).length < 32) {
return toShortString(value);
} else {
StorageSlot.getStringSlot(store).value = value;
return ShortString.wrap(FALLBACK_SENTINEL);
}
}
/**
* @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
*/
function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
return toString(value);
} else {
return store;
}
}
/**
* @dev Return the length of a string that was encoded to `ShortString` or written to storage using
* {setWithFallback}.
*
* WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
* actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
*/
function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
return byteLength(value);
} else {
return bytes(store).length;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}{
"remappings": [
"ds-test/=node_modules/ds-test/src/",
"forge-std/=node_modules/forge-std/src/",
"@openzeppelin/=node_modules/@openzeppelin/",
"@openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/",
"@create3-deployer/=lib/create3-deployer/",
"base64-sol/=node_modules/base64-sol/",
"create3-deployer/=lib/create3-deployer/",
"erc6551/=node_modules/erc6551/",
"eth-gas-reporter/=node_modules/eth-gas-reporter/",
"hardhat-deploy/=node_modules/hardhat-deploy/",
"hardhat/=node_modules/hardhat/",
"openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
"solady/=node_modules/solady/",
"solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/"
],
"optimizer": {
"enabled": true,
"runs": 20000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"viaIR": false,
"libraries": {
"test/foundry/utils/Users.t.sol": {
"UsersLib": "0x4790fd9437BfBDfB46bce1d48BcB90F4d3611F51"
}
}
}Contract ABI
API[{"inputs":[{"internalType":"address","name":"accessController","type":"address"},{"internalType":"address","name":"ipAssetRegistry","type":"address"},{"internalType":"address","name":"licenseRegistry","type":"address"},{"internalType":"address","name":"moduleRegistry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"module","type":"address"}],"name":"IPAccountStorage__NotRegisteredModule","type":"error"},{"inputs":[],"name":"IPAccount__ExpiredSignature","type":"error"},{"inputs":[],"name":"IPAccount__InvalidCalldata","type":"error"},{"inputs":[],"name":"IPAccount__InvalidSignature","type":"error"},{"inputs":[],"name":"IPAccount__InvalidSigner","type":"error"},{"inputs":[],"name":"IPAccount__ZeroAccessController","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"Executed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"deadline","type":"uint256"},{"indexed":true,"internalType":"address","name":"signer","type":"address"},{"indexed":false,"internalType":"bytes","name":"signature","type":"bytes"}],"name":"ExecutedWithSig","type":"event"},{"inputs":[],"name":"ACCESS_CONTROLLER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"IP_ASSET_REGISTRY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LICENSE_REGISTRY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MODULE_REGISTRY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"bytes32Data","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"bytesData","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"execute","outputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"address","name":"signer","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"executeWithSig","outputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"getBytes","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"namespace","type":"bytes32"},{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"getBytes","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"namespace","type":"bytes32"},{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"getBytes32","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"getBytes32","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"isValidSigner","outputs":[{"internalType":"bytes4","name":"","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":"pure","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":"pure","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":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"bytes","name":"value","type":"bytes"}],"name":"setBytes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"bytes32","name":"value","type":"bytes32"}],"name":"setBytes32","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"state","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.