From 8f070db5442e33328bc454b98f4c3a40a3c6fbcb Mon Sep 17 00:00:00 2001 From: wei Date: Thu, 2 Jan 2025 21:45:52 +0800 Subject: [PATCH 01/34] erc7844-draft --- ERCS/erc-7844.md | 587 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 587 insertions(+) create mode 100644 ERCS/erc-7844.md diff --git a/ERCS/erc-7844.md b/ERCS/erc-7844.md new file mode 100644 index 00000000000..948cfa055fb --- /dev/null +++ b/ERCS/erc-7844.md @@ -0,0 +1,587 @@ +--- +eip: 7844 +title: An NFT Standard for AI Agents with Private Metadata +description: A standard interface for AI agent NFTs, enabling secure management of private metadata with verifiable data transfer +author: Ming Wu(@sparkmiw), Jason Zeng(@zenghbo), Wei Wu(@Wilbert957), Michael(@michaelomg) +discussions-to: +status: Draft +type: Standards Track +category: ERC +created: 2025-01-02 +--- + +## Abstract + +A standard interface for NFTs specifically designed for AI agents, where the metadata represents agent capabilities and requires privacy protection. Unlike traditional NFT standards that focus on static metadata, this standard introduces mechanisms for verifiable data ownership and secure transfer. By defining a unified interface for different verification methods (e.g., TEE, ZKP), it enables secure management of valuable agent metadata such as models, memory, and character definitions, while maintaining confidentiality and verifiability. + +## Motivation + +With the increasing intelligence of AI models, agents have become powerful tools for automating meaningful daily tasks. The integration of agents with blockchain technology has been recognized as a major narrative in the crypto industry, with many projects enabling agent creation for their users. However, a crucial missing piece is the decentralized management of agent ownership. + +AI agents possess inherent non-fungible properties that make them natural candidates for NFT representation: + +1. Each agent is unique, with its own model, memory, and character +2. Agents embody clear ownership rights, representing significant computational investment and intellectual property +3. Agents have private metadata (e.g., neural network models, memory, character definitions) that defines their capabilities + +However, current NFT standards like ERC-721 are insufficient for representing AI agents effectively. The key challenge lies in the metadata transfer mechanism. Unlike traditional NFTs where metadata is typically static and publicly accessible, an agent's metadata: + +1. Has intrinsic value and is often the primary purpose of the transfer +2. Requires encrypted storage to protect intellectual property +3. Needs privacy-preserving and verifiable transfer mechanisms when ownership changes + +For example, when transferring an agent NFT, we need to ensure: + +1. The actual transfer of encrypted metadata is verifiable +2. The new owner can securely access the metadata +3. The agent's execution environment can verify ownership and load appropriate metadata + +This EIP introduces a standard for NFTs with private metadata that addresses these requirements through privacy-preserving verification mechanisms, enabling secure ownership and transfer of valuable agent data while maintaining confidentiality and verifiability. This standard will serve as a foundation for the emerging agent ecosystem, allowing platforms to provide verifiable agent ownership and secure metadata management in a decentralized manner. + +## Specification + +The EIP defines three key interfaces: the main NFT interface (IERC7844), the metadata interface (IERC7844Metadata), and the data verification interface (IERC7844DataVerifier). + +### Data Verification System + +The verification system consists of two core components that work together to ensure secure data operations: + +1. **On-chain Verifier (IERC7844DataVerifier)** + - Implemented as a smart contract + - Verifies proofs submitted through contract calls + - Returns structured verification results + - Can be implemented using different verification mechanisms (TEE/ZKP) + +2. **Off-chain Prover** + - Generates proofs for ownership and availability claims + - Works with encrypted data and keys + - Implementation varies based on verification mechanism: + * TEE-based: Generates proofs within trusted hardware + * ZKP-based: Creates cryptographic zero-knowledge proofs + +The system supports two types of proofs: + +1. **Ownership Proof** + - Generated by Prover with access to original data + - Proves knowledge of pre-images for claimed dataHashes + - Verified on-chain through verifyOwnership() + +2. **Transfer Validity Proof** + - Generated by Prover for data transfers + - Proves: + * Knowledge of original data (pre-images) + * Correct decryption and re-encryption of data + * Secure key transmission (using receiver's public key to encrypt the new key) + * Data availability in storage (using receiver's signature to confirm the data is available in storage) + - Verified on-chain through verifyTransferValidity() + +The ownership verification is optional because when the minted token is transferred or cloned, the ownership verification is checked again inside the availability verification. But for better safe than sorry, we recommend using ownership verification for minting and updates. + +Different verification mechanisms have distinct capabilities: + +- **TEE-based Implementation** + * Prover runs in trusted hardware + * Can handle private keys securely + * Enables direct data re-encryption + * Verifier checks TEE attestations + +- **ZKP-based Implementation** + * Prover generates cryptographic proofs + * Cannot handle multi-party private keys + * Re-encryption key known to prover + * Requires additional re-encryption when next update, otherwise the new update is still visible to the prover + +### Data Verification Interface + +```solidity +struct OwnershipProofOutput { + bytes32[] dataHashes; + bool isValid; +} + +struct TransferValidityProofOutput { + bytes32[] oldDataHashes; + bytes32[] newDataHashes; + bytes pubKey; + bytes sealedKey; + bool isValid; +} + +interface IERC7844DataVerifier { + /// @notice Verify ownership of data + /// @param _proof Proof generated by companion prover system + /// @return Verification result and validated data hashes + function verifyOwnership( + bytes calldata _proof + ) external view returns (OwnershipProofOutput memory); + + /// @notice Verify data transfer validity + /// @param _proof Proof generated by companion prover system + /// @return Verification result and transfer details + function verifyTransferValidity( + bytes calldata _proof + ) external view returns (TransferValidityProofOutput memory); +} +``` + +### Metadata Interface + +```solidity +interface IERC7844Metadata { + /// @dev This emits when data is updated + event Updated( + uint256 indexed _tokenId, + bytes32[] _oldDataHashes, + bytes32[] _newDataHashes + ); + + /// @notice Get the name of the NFT collection + function name() external view returns (string memory); + + /// @notice Get the symbol of the NFT collection + function symbol() external view returns (string memory); + + /// @notice Get the config metadata for the NFT collection + /// @return storageURL The base storage URL for the functional data + function config() external view returns ( + string memory storageURL + ); + + /// @notice Set the config metadata (only owner) + function setConfig( + string memory storageURL + ) external; + + /// @notice Get the metadata URI for a specific token + function tokenURI(uint256 tokenId) external view returns (string memory); + + /// @notice Update data + /// @param _tokenId The token to update + /// @param _proof Proof of updated data ownership + function update( + uint256 _tokenId, + bytes calldata _proof + ) external; + + /// @notice Get the data hash of a token + /// @param _tokenId The token identifier + /// @return The current data hash of the token + function dataHashesOf(uint256 _tokenId) external view returns (bytes32[] memory); +} + +``` + +### Main NFT Interface + +```solidity +interface IERC7844 { + /// @dev This emits when a new functional NFT is minted + event Minted( + uint256 indexed _tokenId, + address indexed _creator, + bytes32[] _dataHashes + ); + + /// @dev This emits when a user is authorized to use the data + event AuthorizedUsage( + uint256 indexed _tokenId, + address indexed _user + ); + + /// @dev This emits when data is transferred with ownership + event Transferred( + uint256 _tokenId, + address indexed _from, + address indexed _to + ); + + /// @dev This emits when data is cloned + event Cloned( + uint256 indexed _tokenId, + uint256 indexed _newTokenId, + address _from, + address _to + ); + + /// @dev This emits when a sealed key is published + event PublishedSealedKey( + address indexed _to, + uint256 indexed _tokenId, + bytes _sealedKey + ); + + /// @notice The verifier interface that this NFT uses + /// @return The address of the verifier contract + function verifier() external view returns (IERC7844DataVerifier); + + /// @notice Mint new NFT with data ownership proof + /// @param _proof Proof of data ownership + /// @return _tokenId The ID of the newly minted token + function mint(bytes calldata _proof) + external + payable + returns (uint256 _tokenId); + + /// @notice Transfer full data (full means data and ownership) + /// @param _to Address to transfer data to + /// @param _tokenId The token to transfer data for + /// @param _proof Proof of data available for _to + function transfer( + address _to, + uint256 _tokenId, + bytes calldata _proof + ) external; + + /// @notice Clone data (clone means just data, not ownership) + /// @param _to Address to clone data to + /// @param _tokenId The token to clone data for + /// @param _proof Proof of data available for _to + /// @return _newTokenId The ID of the newly cloned token + function clone( + address _to, + uint256 _tokenId, + bytes calldata _proof + ) external payable returns (uint256 _newTokenId); + + /// @notice Transfer public data with ownership + /// @param _to Address to transfer data to + /// @param _tokenId The token to transfer data for + function transferPublic( + address _to, + uint256 _tokenId + ) external; + + /// @notice Clone public data + /// @param _to Address to clone data to + /// @param _tokenId The token to clone data for + /// @return _newTokenId The ID of the newly cloned token + function clonePublic( + address _to, + uint256 _tokenId + ) external payable returns (uint256 _newTokenId); + + /// @notice Add authorized user to group + /// @param _tokenId The token to add to group + function authorizeUsage( + uint256 _tokenId, + address _user + ) external; + + /// @notice Get token owner + /// @param _tokenId The token identifier + /// @return The current owner of the token + function ownerOf(uint256 _tokenId) external view returns (address); + + /// @notice Get the authorized users of a token + /// @param _tokenId The token identifier + /// @return The current authorized users of the token + function authorizedUsersOf(uint256 _tokenId) external view returns (address[] memory); +} +``` + +## Rationale + +The design choices in this standard are motivated by several key requirements: + +1. **Verification Abstraction**: The standard separates the verification logic into a dedicated interface (IERC7844DataVerifier), allowing different verification mechanisms (TEE, ZKP) to be implemented and used interchangeably. The verfier should support two types of proof: + - Ownership Proof Verifies that the prover possesses the original data by demonstrating knowledge of the pre-images that generate the claimed dataHashes + - Transfer Validity Proof Verifies secure data integrity and availability by proving: knowledge of the original data (pre-images of oldDataHashes); ability to decrypt with oldKey and re-encrypt with newKey; secure transmission of newKey using recipient's public key; integrity of the newly encrypted data matching newDataHashes; and data availability confirmed by recipient's signature on both oldDataHashes and newDataHashes +2. **Data Protection**: The standard uses data hashes and encrypted keys to ensure that valuable NFT data remains protected while still being integrity and availability verifiable + +3. **Flexible Data Management**: Three distinct data operations are supported: + - Full transfer, where the data and ownership are transferred to the new owner + - Data cloning, where the data is cloned to a new token but the ownership is not transferred + - Data usage authorization, where the data is authorized to be used by a specific user, but the ownership is not transferred, and the user still cannot access the data. This need an environment to authenticate the user and process the request from the authorized user secretly, we call it "Sealed Executor" + +4. **Sealed Executor**: Although the Sealed Executor is not defined and out of the scope of this standard, it is a crucial component for the standard to work. The Sealed Executor is an environment that can authenticate the user and process the request from the authorized user secretly. The Sealed Executor should get authorized group by tokenId, and the verify the signature of the user using the public keys in the authorized group. If the verification is successful, the executor will process the request and return the result to the user, and the sealed executor could be implemented by a trusted party (where permitted), TEE, or FHE + +## Backwards Compatibility + +This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement ERC-721 if traditional NFT compatibility is desired. + +## Reference Implementation + +```solidity +abstract contract BaseVerifier is IERC7844DataVerifier { + // prevent replay attack + mapping(bytes32 => bool) internal usedProofs; + + // check and mark proof used + function _checkAndMarkProof(bytes32 proofHash) internal { + require(!usedProofs[proofHash], "Proof already used"); + usedProofs[proofHash] = true; + } +} + +contract TEEVerifier is BaseVerifier { + address public immutable attestationContract; + + constructor(address _attestationContract) { + attestationContract = _attestationContract; + } + + /// @notice Verify ownership of data, the _proof prove: + /// 1. The pre-image of dataHashes + /// @param proof Proof generated by TEE + function verifyOwnership( + bytes calldata proof + ) external view override returns (OwnershipProofOutput memory) { + // TODO: Implement actual verification logic + return OwnershipProofOutput(new bytes32[](0), true); + } + + /// @notice Verify data transfer validity, the _proof prove: + /// 1. The pre-image of oldDataHashes + /// 2. The oldKey can decrypt the pre-image and the new key re-encrypt the plaintexts to new ciphertexts + /// 3. The newKey is encrypted using the receiver's pubKey + /// 4. The hashes of new ciphertexts is newDataHashes (key to note: TEE could support a private key of the receiver) + /// 5. The newDataHashes identified ciphertexts are available in the storage: need the signature from the receiver signing oldDataHashes and newDataHashes + /// @param proof Proof generated by TEE + function verifyTransferValidity( + bytes calldata proof + ) external view override returns (TransferValidityProofOutput memory) { + // TODO: Implement actual verification logic + return TransferValidityProofOutput(new bytes32[](0), new bytes32[](0), bytes("null"), bytes("null"), true); + } +} + +contract AgentNFT is IERC7844, IERC7844Metadata { + struct TokenData { + address owner; + bytes32[] dataHashes; + address[] authorizedUsers; + } + + IERC7844DataVerifier private immutable _verifier; + mapping(uint256 => TokenData) private _tokens; + uint256 private _nextTokenId; + + string private _name; + string private _symbol; + string private _storageURL; + address private immutable _owner; + + constructor( + string memory name_, + string memory symbol_, + address verifierAddr, + string memory storageURL_ + ) { + require(verifierAddr != address(0), "Zero address"); + _verifier = IERC7844DataVerifier(verifierAddr); + _name = name_; + _symbol = symbol_; + _storageURL = storageURL_; + _owner = msg.sender; + } + + function name() external view returns (string memory) { + return _name; + } + + function symbol() external view returns (string memory) { + return _symbol; + } + + function config() external view returns (string memory) { + return _storageURL; + } + + function setConfig(string memory storageURL) external { + require(msg.sender == _owner, "Not owner"); + _storageURL = storageURL; + } + + function tokenURI(uint256 tokenId) external view returns (string memory) { + require(_exists(tokenId), "Token does not exist"); + return string(abi.encodePacked(_storageURL)); + } + + function update( + uint256 tokenId, + bytes calldata proof + ) external { + TokenData storage token = _tokens[tokenId]; + require(token.owner == msg.sender, "Not owner"); + + OwnershipProofOutput memory proofOupt = _verifier.verifyOwnership(proof); + bytes32[] memory newDataHashes = proofOupt.dataHashes; + require( + proofOupt.isValid, + "Invalid ownership proof" + ); + + bytes32[] memory oldDataHashes = token.dataHashes; + token.dataHashes = newDataHashes; + + emit Updated(tokenId, oldDataHashes, newDataHashes); + } + + function dataHashesOf(uint256 tokenId) external view returns (bytes32[] memory) { + TokenData storage token = _tokens[tokenId]; + require(token.owner != address(0), "Token not exist"); + return token.dataHashes; + } + + function verifier() external view returns (IERC7844DataVerifier) { + return _verifier; + } + + function mint(bytes calldata proof) + external + payable + returns (uint256 tokenId) + { + OwnershipProofOutput memory proofOupt = _verifier.verifyOwnership(proof); + bytes32[] memory dataHashes = proofOupt.dataHashes; + require( + proofOupt.isValid, + "Invalid ownership proof" + ); + + tokenId = _nextTokenId++; + _tokens[tokenId] = TokenData({ + owner: msg.sender, + dataHashes: dataHashes, + authorizedUsers: new address[](0) + }); + + emit Minted(tokenId, msg.sender, dataHashes); + } + + function transfer( + address to, + uint256 tokenId, + bytes calldata proof + ) external { + require(to != address(0), "Zero address"); + + TokenData storage token = _tokens[tokenId]; + require(token.owner == msg.sender, "Not owner"); + + TransferValidityProofOutput memory proofOupt = _verifier.verifyTransferValidity(proof); + bytes32[] memory oldDataHashes = proofOupt.oldDataHashes; + bytes32[] memory newDataHashes = proofOupt.newDataHashes; + bytes32[] memory tokenDataHashes = token.dataHashes; + bytes memory pubKey = proofOupt.pubKey; + bytes memory sealedKey = proofOupt.sealedKey; + + require( + proofOupt.isValid + && _isEqual(oldDataHashes, tokenDataHashes) + && _pubKeyToAddress(pubKey) == to, + "Invalid transfer validity proof" + ); + + token.owner = to; + token.dataHashes = newDataHashes; + + emit Transferred(tokenId, msg.sender, to); + emit PublishedSealedKey(to, tokenId, sealedKey); + } + + function transferPublic( + address to, + uint256 tokenId + ) external { + require(to != address(0), "Zero address"); + require(_tokens[tokenId].owner == msg.sender, "Not owner"); + _tokens[tokenId].owner = to; + emit Transferred(tokenId, msg.sender, to); + } + + function clone( + address to, + uint256 tokenId, + bytes calldata proof + ) external payable returns (uint256) { + require(to != address(0), "Zero address"); + + require(_tokens[tokenId].owner == msg.sender, "Not owner"); + + TransferValidityProofOutput memory proofOupt = _verifier.verifyTransferValidity(proof); + bytes32[] memory oldDataHashes = proofOupt.oldDataHashes; + bytes32[] memory newDataHashes = proofOupt.newDataHashes; + bytes32[] memory tokenDataHashes = _tokens[tokenId].dataHashes; + bytes memory pubKey = proofOupt.pubKey; + bytes memory sealedKey = proofOupt.sealedKey; + + require( + proofOupt.isValid + && _isEqual(oldDataHashes, tokenDataHashes) + && _pubKeyToAddress(pubKey) == to, + "Invalid transfer validity proof" + ); + + uint256 newTokenId = _nextTokenId++; + _tokens[newTokenId] = TokenData({ + owner: to, + dataHashes: newDataHashes, + authorizedUsers: new address[](0) + }); + + emit Cloned(tokenId, newTokenId, msg.sender, to); + emit PublishedSealedKey(to, newTokenId, sealedKey); + return newTokenId; + } + + function clonePublic( + address to, + uint256 tokenId + ) external payable returns (uint256) { + require(to != address(0), "Zero address"); + require(_tokens[tokenId].owner == msg.sender, "Not owner"); + + uint256 newTokenId = _nextTokenId++; + _tokens[newTokenId] = TokenData({ + owner: to, + dataHashes: _tokens[tokenId].dataHashes, + authorizedUsers: new address[](0) + }); + emit Cloned(tokenId, newTokenId, msg.sender, to); + return newTokenId; + } + + function authorizeUsage(uint256 tokenId, address user) external { + require(_tokens[tokenId].owner == msg.sender, "Not owner"); + _tokens[tokenId].authorizedUsers.push(user); + emit AuthorizedUsage(tokenId, user); + } + + function ownerOf(uint256 tokenId) external view returns (address) { + TokenData storage token = _tokens[tokenId]; + require(token.owner != address(0), "Token not exist"); + return token.owner; + } + + function authorizedUsersOf(uint256 tokenId) external view returns (address[] memory) { + TokenData storage token = _tokens[tokenId]; + require(token.owner != address(0), "Token not exist"); + return token.authorizedUsers; + } +} +``` + +## Security Considerations + +1. **Proof Verification** + - Implementations must carefully verify all assertions in the proof + - Replay attacks must be prevented + - Different verfication systems have their own security considerations, and distinct capabilities regarding key management: TEE can securely handle private keys from multi-parties, enabling direct data re-encryption. However, ZKP, due to its cryptographic nature, cannot process private keys from multi-parties. As a result, the re-encryption key is also from the prover (i.e., the sender), so tokens acquired through transfer or cloning must undergo re-encryption during their next update, otherwise the new update is still visible to the previous owner. This distinction in key handling capabilities affects how data transformations are managed during later usage +2. **Data Privacy** + - Only hashes and sealed keys are stored on-chain, actual functional data must be stored and transmitted securely off-chain + - Key management is crucial for secure data access + - TEE verification system could support private key of the receiver, but ZKP verification system could not. So when using ZKP, the token transfered or cloned from other should be re-encrypted when next update, otherwise the new update is still visible to the previous owner +3. **Access Control and State Managementl** + - Operations restricted to token owners only + - All data operations must maintain integrity and availability + - Critical state changes (sealed keys, ownership, permissions) must be atomic and verifiable +4. **Sealed Executor** + - Although out of scope for this standard, the Sealed Executor is crucial for secure operation + - The Sealed Executor authenticates users and processes requests in a secure environment by verifying user signatures against authorized public keys for each tokenId + - The Sealed Executor can be implemented through a trusted party (where permitted), Trusted Execution Environment (TEE), or Fully Homomorphic Encryption (FHE) + - Ensuring secure request processing and result delivery + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file From befe45ee7266d3f81ede64c6afb55c35143b3ea5 Mon Sep 17 00:00:00 2001 From: wei Date: Thu, 2 Jan 2025 22:10:37 +0800 Subject: [PATCH 02/34] update author info --- ERCS/erc-7844.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7844.md b/ERCS/erc-7844.md index 948cfa055fb..92d72925f18 100644 --- a/ERCS/erc-7844.md +++ b/ERCS/erc-7844.md @@ -2,7 +2,7 @@ eip: 7844 title: An NFT Standard for AI Agents with Private Metadata description: A standard interface for AI agent NFTs, enabling secure management of private metadata with verifiable data transfer -author: Ming Wu(@sparkmiw), Jason Zeng(@zenghbo), Wei Wu(@Wilbert957), Michael(@michaelomg) +author: Ming Wu(@sparkmiw), Jason Zeng(@zenghbo), Wei Wu(@Wilbert957), Michael Heinrich(@michaelomg) discussions-to: status: Draft type: Standards Track From d4db3ead40b3e4dbfddfc97d72218a39e8ff92b8 Mon Sep 17 00:00:00 2001 From: wei Date: Thu, 2 Jan 2025 22:26:36 +0800 Subject: [PATCH 03/34] update author info; add link for ERC721 --- ERCS/erc-7844.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ERCS/erc-7844.md b/ERCS/erc-7844.md index 92d72925f18..9f602601a75 100644 --- a/ERCS/erc-7844.md +++ b/ERCS/erc-7844.md @@ -2,7 +2,7 @@ eip: 7844 title: An NFT Standard for AI Agents with Private Metadata description: A standard interface for AI agent NFTs, enabling secure management of private metadata with verifiable data transfer -author: Ming Wu(@sparkmiw), Jason Zeng(@zenghbo), Wei Wu(@Wilbert957), Michael Heinrich(@michaelomg) +author: Ming Wu(@sparkmiw), Jason Zeng(@zenghbo), Wei Wu(@Wilbert957) , Michael Heinrich(@michaelomg) discussions-to: status: Draft type: Standards Track @@ -24,7 +24,7 @@ AI agents possess inherent non-fungible properties that make them natural candid 2. Agents embody clear ownership rights, representing significant computational investment and intellectual property 3. Agents have private metadata (e.g., neural network models, memory, character definitions) that defines their capabilities -However, current NFT standards like ERC-721 are insufficient for representing AI agents effectively. The key challenge lies in the metadata transfer mechanism. Unlike traditional NFTs where metadata is typically static and publicly accessible, an agent's metadata: +However, current NFT standards like [ERC-721](../ERCS/erc-721.md) are insufficient for representing AI agents effectively. The key challenge lies in the metadata transfer mechanism. Unlike traditional NFTs where metadata is typically static and publicly accessible, an agent's metadata: 1. Has intrinsic value and is often the primary purpose of the transfer 2. Requires encrypted storage to protect intellectual property @@ -297,7 +297,7 @@ The design choices in this standard are motivated by several key requirements: ## Backwards Compatibility -This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement ERC-721 if traditional NFT compatibility is desired. +This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721](../ERCS/erc-721.md) if traditional NFT compatibility is desired. ## Reference Implementation From 3c73ee1020fd5199b747e505c18bd79bdd2b6be9 Mon Sep 17 00:00:00 2001 From: wei Date: Thu, 2 Jan 2025 22:39:23 +0800 Subject: [PATCH 04/34] shorter title --- ERCS/erc-7844.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7844.md b/ERCS/erc-7844.md index 9f602601a75..1326211ba41 100644 --- a/ERCS/erc-7844.md +++ b/ERCS/erc-7844.md @@ -1,6 +1,6 @@ --- eip: 7844 -title: An NFT Standard for AI Agents with Private Metadata +title: An NFT Standard for AI Agents description: A standard interface for AI agent NFTs, enabling secure management of private metadata with verifiable data transfer author: Ming Wu(@sparkmiw), Jason Zeng(@zenghbo), Wei Wu(@Wilbert957) , Michael Heinrich(@michaelomg) discussions-to: From f998eb43547dbbe13a87e4e792582c62f15dccac Mon Sep 17 00:00:00 2001 From: wei Date: Thu, 2 Jan 2025 22:46:48 +0800 Subject: [PATCH 05/34] 1.remove words 'standard' in description; re-format author info --- ERCS/erc-7844.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7844.md b/ERCS/erc-7844.md index 1326211ba41..11cb19541ef 100644 --- a/ERCS/erc-7844.md +++ b/ERCS/erc-7844.md @@ -1,8 +1,8 @@ --- eip: 7844 title: An NFT Standard for AI Agents -description: A standard interface for AI agent NFTs, enabling secure management of private metadata with verifiable data transfer -author: Ming Wu(@sparkmiw), Jason Zeng(@zenghbo), Wei Wu(@Wilbert957) , Michael Heinrich(@michaelomg) +description: An interface for AI agent NFTs, enabling secure management of private metadata with verifiable data transfer +author: Ming Wu (@sparkmiw), Jason Zeng (@zenghbo), Wei Wu (@Wilbert957), Michael Heinrich (@michaelomg) discussions-to: status: Draft type: Standards Track From 357882ad5ac9e497fa7a99f1783dd338cdd392b1 Mon Sep 17 00:00:00 2001 From: wei Date: Thu, 2 Jan 2025 22:49:41 +0800 Subject: [PATCH 06/34] remove extra whitespace --- ERCS/erc-7844.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ERCS/erc-7844.md b/ERCS/erc-7844.md index 11cb19541ef..23766e29a7d 100644 --- a/ERCS/erc-7844.md +++ b/ERCS/erc-7844.md @@ -1,13 +1,13 @@ --- -eip: 7844 +eip: 7844 title: An NFT Standard for AI Agents description: An interface for AI agent NFTs, enabling secure management of private metadata with verifiable data transfer author: Ming Wu (@sparkmiw), Jason Zeng (@zenghbo), Wei Wu (@Wilbert957), Michael Heinrich (@michaelomg) -discussions-to: -status: Draft -type: Standards Track -category: ERC -created: 2025-01-02 +discussions-to: +status: Draft +type: Standards Track +category: ERC +created: 2025-01-02 --- ## Abstract From afd37df141f5cb05e615c612f47e4709d78d4379 Mon Sep 17 00:00:00 2001 From: wei Date: Thu, 2 Jan 2025 22:52:25 +0800 Subject: [PATCH 07/34] remove words 'strandard' in the title --- ERCS/erc-7844.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7844.md b/ERCS/erc-7844.md index 23766e29a7d..c2818ace378 100644 --- a/ERCS/erc-7844.md +++ b/ERCS/erc-7844.md @@ -1,7 +1,7 @@ --- eip: 7844 -title: An NFT Standard for AI Agents -description: An interface for AI agent NFTs, enabling secure management of private metadata with verifiable data transfer +title: NFT Interface for AI Agents +description: Interface for AI agent NFTs, enabling secure management of private metadata with verifiable data transfer author: Ming Wu (@sparkmiw), Jason Zeng (@zenghbo), Wei Wu (@Wilbert957), Michael Heinrich (@michaelomg) discussions-to: status: Draft From 83253e96cec4356ff9d5a6a67b1d085fd0c892f6 Mon Sep 17 00:00:00 2001 From: wei Date: Mon, 6 Jan 2025 20:28:47 +0800 Subject: [PATCH 08/34] add discussions-to --- ERCS/erc-7844.md | 335 +++++++++++++++++++++++------------------------ 1 file changed, 165 insertions(+), 170 deletions(-) diff --git a/ERCS/erc-7844.md b/ERCS/erc-7844.md index c2818ace378..f49a4a78688 100644 --- a/ERCS/erc-7844.md +++ b/ERCS/erc-7844.md @@ -1,9 +1,9 @@ --- -eip: 7844 +eip: 7857 title: NFT Interface for AI Agents description: Interface for AI agent NFTs, enabling secure management of private metadata with verifiable data transfer author: Ming Wu (@sparkmiw), Jason Zeng (@zenghbo), Wei Wu (@Wilbert957), Michael Heinrich (@michaelomg) -discussions-to: +discussions-to: https://ethereum-magicians.org/t/erc-7857-an-nft-standard-for-ai-agents-with-private-metadata/22391 status: Draft type: Standards Track category: ERC @@ -40,13 +40,13 @@ This EIP introduces a standard for NFTs with private metadata that addresses the ## Specification -The EIP defines three key interfaces: the main NFT interface (IERC7844), the metadata interface (IERC7844Metadata), and the data verification interface (IERC7844DataVerifier). +The EIP defines three key interfaces: the main NFT interface, the metadata interface, and the data verification interface. ### Data Verification System The verification system consists of two core components that work together to ensure secure data operations: -1. **On-chain Verifier (IERC7844DataVerifier)** +1. **On-chain Verifier (data verification interface)** - Implemented as a smart contract - Verifies proofs submitted through contract calls - Returns structured verification results @@ -94,11 +94,13 @@ Different verification mechanisms have distinct capabilities: ### Data Verification Interface ```solidity +/// @notice Output of ownership proof verification struct OwnershipProofOutput { bytes32[] dataHashes; bool isValid; } +/// @notice Output of transfer validity proof verification struct TransferValidityProofOutput { bytes32[] oldDataHashes; bytes32[] newDataHashes; @@ -107,183 +109,176 @@ struct TransferValidityProofOutput { bool isValid; } -interface IERC7844DataVerifier { - /// @notice Verify ownership of data - /// @param _proof Proof generated by companion prover system - /// @return Verification result and validated data hashes - function verifyOwnership( - bytes calldata _proof - ) external view returns (OwnershipProofOutput memory); - - /// @notice Verify data transfer validity - /// @param _proof Proof generated by companion prover system - /// @return Verification result and transfer details - function verifyTransferValidity( - bytes calldata _proof - ) external view returns (TransferValidityProofOutput memory); -} +/// @notice Verify ownership of data +/// @param _proof Proof generated by companion prover system +/// @return Verification result and validated data hashes +function verifyOwnership( + bytes calldata _proof +) external view returns (OwnershipProofOutput memory); + +/// @notice Verify data transfer validity +/// @param _proof Proof generated by companion prover system +/// @return Verification result and transfer details +function verifyTransferValidity( + bytes calldata _proof +) external view returns (TransferValidityProofOutput memory); ``` ### Metadata Interface ```solidity -interface IERC7844Metadata { - /// @dev This emits when data is updated - event Updated( - uint256 indexed _tokenId, - bytes32[] _oldDataHashes, - bytes32[] _newDataHashes - ); - - /// @notice Get the name of the NFT collection - function name() external view returns (string memory); - - /// @notice Get the symbol of the NFT collection - function symbol() external view returns (string memory); - - /// @notice Get the config metadata for the NFT collection - /// @return storageURL The base storage URL for the functional data - function config() external view returns ( - string memory storageURL - ); - - /// @notice Set the config metadata (only owner) - function setConfig( - string memory storageURL - ) external; - - /// @notice Get the metadata URI for a specific token - function tokenURI(uint256 tokenId) external view returns (string memory); - - /// @notice Update data - /// @param _tokenId The token to update - /// @param _proof Proof of updated data ownership - function update( - uint256 _tokenId, - bytes calldata _proof - ) external; - - /// @notice Get the data hash of a token - /// @param _tokenId The token identifier - /// @return The current data hash of the token - function dataHashesOf(uint256 _tokenId) external view returns (bytes32[] memory); -} - +/// @dev This emits when data is updated +event Updated( + uint256 indexed _tokenId, + bytes32[] _oldDataHashes, + bytes32[] _newDataHashes +); + +/// @notice Get the name of the NFT collection +function name() external view returns (string memory); + +/// @notice Get the symbol of the NFT collection +function symbol() external view returns (string memory); + +/// @notice Get the config metadata for the NFT collection +/// @return storageURL The base storage URL for the functional data +function config() external view returns ( + string memory storageURL +); + +/// @notice Set the config metadata (only owner) +function setConfig( + string memory storageURL +) external; + +/// @notice Get the metadata URI for a specific token +function tokenURI(uint256 tokenId) external view returns (string memory); + +/// @notice Update data +/// @param _tokenId The token to update +/// @param _proof Proof of updated data ownership +function update( + uint256 _tokenId, + bytes calldata _proof +) external; + +/// @notice Get the data hash of a token +/// @param _tokenId The token identifier +/// @return The current data hash of the token +function dataHashesOf(uint256 _tokenId) external view returns (bytes32[] memory); ``` ### Main NFT Interface ```solidity -interface IERC7844 { - /// @dev This emits when a new functional NFT is minted - event Minted( - uint256 indexed _tokenId, - address indexed _creator, - bytes32[] _dataHashes - ); - - /// @dev This emits when a user is authorized to use the data - event AuthorizedUsage( - uint256 indexed _tokenId, - address indexed _user - ); - - /// @dev This emits when data is transferred with ownership - event Transferred( - uint256 _tokenId, - address indexed _from, - address indexed _to - ); - - /// @dev This emits when data is cloned - event Cloned( - uint256 indexed _tokenId, - uint256 indexed _newTokenId, - address _from, - address _to - ); - - /// @dev This emits when a sealed key is published - event PublishedSealedKey( - address indexed _to, - uint256 indexed _tokenId, - bytes _sealedKey - ); - - /// @notice The verifier interface that this NFT uses - /// @return The address of the verifier contract - function verifier() external view returns (IERC7844DataVerifier); - - /// @notice Mint new NFT with data ownership proof - /// @param _proof Proof of data ownership - /// @return _tokenId The ID of the newly minted token - function mint(bytes calldata _proof) - external - payable - returns (uint256 _tokenId); - - /// @notice Transfer full data (full means data and ownership) - /// @param _to Address to transfer data to - /// @param _tokenId The token to transfer data for - /// @param _proof Proof of data available for _to - function transfer( - address _to, - uint256 _tokenId, - bytes calldata _proof - ) external; - - /// @notice Clone data (clone means just data, not ownership) - /// @param _to Address to clone data to - /// @param _tokenId The token to clone data for - /// @param _proof Proof of data available for _to - /// @return _newTokenId The ID of the newly cloned token - function clone( - address _to, - uint256 _tokenId, - bytes calldata _proof - ) external payable returns (uint256 _newTokenId); - - /// @notice Transfer public data with ownership - /// @param _to Address to transfer data to - /// @param _tokenId The token to transfer data for - function transferPublic( - address _to, - uint256 _tokenId - ) external; - - /// @notice Clone public data - /// @param _to Address to clone data to - /// @param _tokenId The token to clone data for - /// @return _newTokenId The ID of the newly cloned token - function clonePublic( - address _to, - uint256 _tokenId - ) external payable returns (uint256 _newTokenId); - - /// @notice Add authorized user to group - /// @param _tokenId The token to add to group - function authorizeUsage( - uint256 _tokenId, - address _user - ) external; - - /// @notice Get token owner - /// @param _tokenId The token identifier - /// @return The current owner of the token - function ownerOf(uint256 _tokenId) external view returns (address); - - /// @notice Get the authorized users of a token - /// @param _tokenId The token identifier - /// @return The current authorized users of the token - function authorizedUsersOf(uint256 _tokenId) external view returns (address[] memory); -} +/// @dev This emits when a new functional NFT is minted +event Minted( + uint256 indexed _tokenId, + address indexed _creator, + bytes32[] _dataHashes +); + +/// @dev This emits when a user is authorized to use the data +event AuthorizedUsage( + uint256 indexed _tokenId, + address indexed _user +); + +/// @dev This emits when data is transferred with ownership +event Transferred( + uint256 _tokenId, + address indexed _from, + address indexed _to +); + +/// @dev This emits when data is cloned +event Cloned( + uint256 indexed _tokenId, + uint256 indexed _newTokenId, + address _from, + address _to +); + +/// @dev This emits when a sealed key is published +event PublishedSealedKey( + address indexed _to, + uint256 indexed _tokenId, + bytes _sealedKey +); + +/// @notice The verifier interface that this NFT uses +/// @return The address of the verifier contract +function verifier() external view returns (IDataVerifier); + +/// @notice Mint new NFT with data ownership proof +/// @param _proof Proof of data ownership +/// @return _tokenId The ID of the newly minted token +function mint(bytes calldata _proof) + external + payable + returns (uint256 _tokenId); + +/// @notice Transfer full data (full means data and ownership) +/// @param _to Address to transfer data to +/// @param _tokenId The token to transfer data for +/// @param _proof Proof of data available for _to +function transfer( + address _to, + uint256 _tokenId, + bytes calldata _proof +) external; + +/// @notice Clone data (clone means just data, not ownership) +/// @param _to Address to clone data to +/// @param _tokenId The token to clone data for +/// @param _proof Proof of data available for _to +/// @return _newTokenId The ID of the newly cloned token +function clone( + address _to, + uint256 _tokenId, + bytes calldata _proof +) external payable returns (uint256 _newTokenId); + +/// @notice Transfer public data with ownership +/// @param _to Address to transfer data to +/// @param _tokenId The token to transfer data for +function transferPublic( + address _to, + uint256 _tokenId +) external; + +/// @notice Clone public data +/// @param _to Address to clone data to +/// @param _tokenId The token to clone data for +/// @return _newTokenId The ID of the newly cloned token +function clonePublic( + address _to, + uint256 _tokenId +) external payable returns (uint256 _newTokenId); + +/// @notice Add authorized user to group +/// @param _tokenId The token to add to group +function authorizeUsage( + uint256 _tokenId, + address _user +) external; + +/// @notice Get token owner +/// @param _tokenId The token identifier +/// @return The current owner of the token +function ownerOf(uint256 _tokenId) external view returns (address); + +/// @notice Get the authorized users of a token +/// @param _tokenId The token identifier +/// @return The current authorized users of the token +function authorizedUsersOf(uint256 _tokenId) external view returns (address[] memory); ``` ## Rationale The design choices in this standard are motivated by several key requirements: -1. **Verification Abstraction**: The standard separates the verification logic into a dedicated interface (IERC7844DataVerifier), allowing different verification mechanisms (TEE, ZKP) to be implemented and used interchangeably. The verfier should support two types of proof: +1. **Verification Abstraction**: The standard separates the verification logic into a dedicated interface (IDataVerifier), allowing different verification mechanisms (TEE, ZKP) to be implemented and used interchangeably. The verfier should support two types of proof: - Ownership Proof Verifies that the prover possesses the original data by demonstrating knowledge of the pre-images that generate the claimed dataHashes - Transfer Validity Proof Verifies secure data integrity and availability by proving: knowledge of the original data (pre-images of oldDataHashes); ability to decrypt with oldKey and re-encrypt with newKey; secure transmission of newKey using recipient's public key; integrity of the newly encrypted data matching newDataHashes; and data availability confirmed by recipient's signature on both oldDataHashes and newDataHashes 2. **Data Protection**: The standard uses data hashes and encrypted keys to ensure that valuable NFT data remains protected while still being integrity and availability verifiable @@ -302,7 +297,7 @@ This EIP does not inherit from existing NFT standards to maintain its focus on f ## Reference Implementation ```solidity -abstract contract BaseVerifier is IERC7844DataVerifier { +abstract contract BaseVerifier is IDataVerifier { // prevent replay attack mapping(bytes32 => bool) internal usedProofs; @@ -345,14 +340,14 @@ contract TEEVerifier is BaseVerifier { } } -contract AgentNFT is IERC7844, IERC7844Metadata { +contract AgentNFT { struct TokenData { address owner; bytes32[] dataHashes; address[] authorizedUsers; } - IERC7844DataVerifier private immutable _verifier; + IDataVerifier private immutable _verifier; mapping(uint256 => TokenData) private _tokens; uint256 private _nextTokenId; @@ -368,7 +363,7 @@ contract AgentNFT is IERC7844, IERC7844Metadata { string memory storageURL_ ) { require(verifierAddr != address(0), "Zero address"); - _verifier = IERC7844DataVerifier(verifierAddr); + _verifier = IDataVerifier(verifierAddr); _name = name_; _symbol = symbol_; _storageURL = storageURL_; @@ -423,7 +418,7 @@ contract AgentNFT is IERC7844, IERC7844Metadata { return token.dataHashes; } - function verifier() external view returns (IERC7844DataVerifier) { + function verifier() external view returns (IDataVerifier) { return _verifier; } From 01793706954880ce0d1ea2b97751bb0f07f469ef Mon Sep 17 00:00:00 2001 From: wei Date: Mon, 6 Jan 2025 21:30:57 +0800 Subject: [PATCH 09/34] rename doc --- ERCS/{erc-7844.md => erc-7857.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ERCS/{erc-7844.md => erc-7857.md} (100%) diff --git a/ERCS/erc-7844.md b/ERCS/erc-7857.md similarity index 100% rename from ERCS/erc-7844.md rename to ERCS/erc-7857.md From 94e6c4eb517b8e53b86b43d0df2691ef49f2514c Mon Sep 17 00:00:00 2001 From: wei Date: Mon, 6 Jan 2025 21:47:31 +0800 Subject: [PATCH 10/34] change erc-721 reference --- ERCS/erc-7857.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index f49a4a78688..c3f5c98af0c 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -24,7 +24,7 @@ AI agents possess inherent non-fungible properties that make them natural candid 2. Agents embody clear ownership rights, representing significant computational investment and intellectual property 3. Agents have private metadata (e.g., neural network models, memory, character definitions) that defines their capabilities -However, current NFT standards like [ERC-721](../ERCS/erc-721.md) are insufficient for representing AI agents effectively. The key challenge lies in the metadata transfer mechanism. Unlike traditional NFTs where metadata is typically static and publicly accessible, an agent's metadata: +However, current NFT standards like [ERC-721](./erc-721.md) are insufficient for representing AI agents effectively. The key challenge lies in the metadata transfer mechanism. Unlike traditional NFTs where metadata is typically static and publicly accessible, an agent's metadata: 1. Has intrinsic value and is often the primary purpose of the transfer 2. Requires encrypted storage to protect intellectual property @@ -292,7 +292,7 @@ The design choices in this standard are motivated by several key requirements: ## Backwards Compatibility -This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721](../ERCS/erc-721.md) if traditional NFT compatibility is desired. +This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721](./erc-721.md) if traditional NFT compatibility is desired. ## Reference Implementation From 71ca8ccfe12e606271ae133183735e11816cb1c9 Mon Sep 17 00:00:00 2001 From: wei Date: Tue, 7 Jan 2025 11:03:21 +0800 Subject: [PATCH 11/34] change erc-721 reference to an absolute url --- ERCS/erc-7857.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index c3f5c98af0c..a4f4385d698 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -24,7 +24,7 @@ AI agents possess inherent non-fungible properties that make them natural candid 2. Agents embody clear ownership rights, representing significant computational investment and intellectual property 3. Agents have private metadata (e.g., neural network models, memory, character definitions) that defines their capabilities -However, current NFT standards like [ERC-721](./erc-721.md) are insufficient for representing AI agents effectively. The key challenge lies in the metadata transfer mechanism. Unlike traditional NFTs where metadata is typically static and publicly accessible, an agent's metadata: +However, current NFT standards like [ERC-721](https://github.com/ethereum/ERCs/blob/master/ERCS/erc-721.md) are insufficient for representing AI agents effectively. The key challenge lies in the metadata transfer mechanism. Unlike traditional NFTs where metadata is typically static and publicly accessible, an agent's metadata: 1. Has intrinsic value and is often the primary purpose of the transfer 2. Requires encrypted storage to protect intellectual property @@ -292,7 +292,7 @@ The design choices in this standard are motivated by several key requirements: ## Backwards Compatibility -This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721](./erc-721.md) if traditional NFT compatibility is desired. +This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721](https://github.com/ethereum/ERCs/blob/master/ERCS/erc-721.md) if traditional NFT compatibility is desired. ## Reference Implementation From 477173cc3bf69c7bff3bcd33bb208c5d36a6341d Mon Sep 17 00:00:00 2001 From: wei Date: Sat, 11 Jan 2025 11:33:38 +0800 Subject: [PATCH 12/34] move reference to 0glabs repo --- ERCS/erc-7857.md | 308 ++++------------------------------------------- 1 file changed, 22 insertions(+), 286 deletions(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index a4f4385d698..98ead754360 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -1,7 +1,7 @@ --- eip: 7857 -title: NFT Interface for AI Agents -description: Interface for AI agent NFTs, enabling secure management of private metadata with verifiable data transfer +title: AI Agents NFT with Private Metadata +description: AI agent NFTs, enabling secure management of private metadata with verifiable data transfer author: Ming Wu (@sparkmiw), Jason Zeng (@zenghbo), Wei Wu (@Wilbert957), Michael Heinrich (@michaelomg) discussions-to: https://ethereum-magicians.org/t/erc-7857-an-nft-standard-for-ai-agents-with-private-metadata/22391 status: Draft @@ -24,7 +24,7 @@ AI agents possess inherent non-fungible properties that make them natural candid 2. Agents embody clear ownership rights, representing significant computational investment and intellectual property 3. Agents have private metadata (e.g., neural network models, memory, character definitions) that defines their capabilities -However, current NFT standards like [ERC-721](https://github.com/ethereum/ERCs/blob/master/ERCS/erc-721.md) are insufficient for representing AI agents effectively. The key challenge lies in the metadata transfer mechanism. Unlike traditional NFTs where metadata is typically static and publicly accessible, an agent's metadata: +However, current NFT standards like [ERC-721](./erc-721.md) are insufficient for representing AI agents effectively. The key challenge lies in the metadata transfer mechanism. Unlike traditional NFTs where metadata is typically static and publicly accessible, an agent's metadata: 1. Has intrinsic value and is often the primary purpose of the transfer 2. Requires encrypted storage to protect intellectual property @@ -140,32 +140,26 @@ function name() external view returns (string memory); /// @notice Get the symbol of the NFT collection function symbol() external view returns (string memory); -/// @notice Get the config metadata for the NFT collection -/// @return storageURL The base storage URL for the functional data -function config() external view returns ( - string memory storageURL -); - -/// @notice Set the config metadata (only owner) -function setConfig( - string memory storageURL -) external; - /// @notice Get the metadata URI for a specific token function tokenURI(uint256 tokenId) external view returns (string memory); /// @notice Update data /// @param _tokenId The token to update -/// @param _proof Proof of updated data ownership +/// @param _proofs Proof of updated data ownership function update( uint256 _tokenId, - bytes calldata _proof + bytes[] calldata _proofs ) external; /// @notice Get the data hash of a token /// @param _tokenId The token identifier /// @return The current data hash of the token -function dataHashesOf(uint256 _tokenId) external view returns (bytes32[] memory); +function dataHashesOf(uint256 _tokenId) public view returns (bytes32[] memory); + +/// @notice Get the data description of a token +/// @param _tokenId The token identifier +/// @return The current data description of the token +function dataDescriptionsOf(uint256 _tokenId) public view returns (string[] memory); ``` ### Main NFT Interface @@ -175,7 +169,8 @@ function dataHashesOf(uint256 _tokenId) external view returns (bytes32[] memory) event Minted( uint256 indexed _tokenId, address indexed _creator, - bytes32[] _dataHashes + bytes32[] _dataHashes, + string[] _dataDescriptions ); /// @dev This emits when a user is authorized to use the data @@ -211,9 +206,10 @@ event PublishedSealedKey( function verifier() external view returns (IDataVerifier); /// @notice Mint new NFT with data ownership proof -/// @param _proof Proof of data ownership +/// @param _proofs Proofs of data ownership +/// @param _dataDescriptions Descriptions of the data /// @return _tokenId The ID of the newly minted token -function mint(bytes calldata _proof) +function mint(bytes[] calldata _proofs, string[] calldata _dataDescriptions) external payable returns (uint256 _tokenId); @@ -221,22 +217,22 @@ function mint(bytes calldata _proof) /// @notice Transfer full data (full means data and ownership) /// @param _to Address to transfer data to /// @param _tokenId The token to transfer data for -/// @param _proof Proof of data available for _to +/// @param _proofs Proofs of data available for _to function transfer( address _to, uint256 _tokenId, - bytes calldata _proof + bytes[] calldata _proofs ) external; /// @notice Clone data (clone means just data, not ownership) /// @param _to Address to clone data to /// @param _tokenId The token to clone data for -/// @param _proof Proof of data available for _to +/// @param _proofs Proofs of data available for _to /// @return _newTokenId The ID of the newly cloned token function clone( address _to, uint256 _tokenId, - bytes calldata _proof + bytes[] calldata _proofs ) external payable returns (uint256 _newTokenId); /// @notice Transfer public data with ownership @@ -292,270 +288,10 @@ The design choices in this standard are motivated by several key requirements: ## Backwards Compatibility -This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721](https://github.com/ethereum/ERCs/blob/master/ERCS/erc-721.md) if traditional NFT compatibility is desired. +This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721](./erc-721.md) if traditional NFT compatibility is desired. ## Reference Implementation - -```solidity -abstract contract BaseVerifier is IDataVerifier { - // prevent replay attack - mapping(bytes32 => bool) internal usedProofs; - - // check and mark proof used - function _checkAndMarkProof(bytes32 proofHash) internal { - require(!usedProofs[proofHash], "Proof already used"); - usedProofs[proofHash] = true; - } -} - -contract TEEVerifier is BaseVerifier { - address public immutable attestationContract; - - constructor(address _attestationContract) { - attestationContract = _attestationContract; - } - - /// @notice Verify ownership of data, the _proof prove: - /// 1. The pre-image of dataHashes - /// @param proof Proof generated by TEE - function verifyOwnership( - bytes calldata proof - ) external view override returns (OwnershipProofOutput memory) { - // TODO: Implement actual verification logic - return OwnershipProofOutput(new bytes32[](0), true); - } - - /// @notice Verify data transfer validity, the _proof prove: - /// 1. The pre-image of oldDataHashes - /// 2. The oldKey can decrypt the pre-image and the new key re-encrypt the plaintexts to new ciphertexts - /// 3. The newKey is encrypted using the receiver's pubKey - /// 4. The hashes of new ciphertexts is newDataHashes (key to note: TEE could support a private key of the receiver) - /// 5. The newDataHashes identified ciphertexts are available in the storage: need the signature from the receiver signing oldDataHashes and newDataHashes - /// @param proof Proof generated by TEE - function verifyTransferValidity( - bytes calldata proof - ) external view override returns (TransferValidityProofOutput memory) { - // TODO: Implement actual verification logic - return TransferValidityProofOutput(new bytes32[](0), new bytes32[](0), bytes("null"), bytes("null"), true); - } -} - -contract AgentNFT { - struct TokenData { - address owner; - bytes32[] dataHashes; - address[] authorizedUsers; - } - - IDataVerifier private immutable _verifier; - mapping(uint256 => TokenData) private _tokens; - uint256 private _nextTokenId; - - string private _name; - string private _symbol; - string private _storageURL; - address private immutable _owner; - - constructor( - string memory name_, - string memory symbol_, - address verifierAddr, - string memory storageURL_ - ) { - require(verifierAddr != address(0), "Zero address"); - _verifier = IDataVerifier(verifierAddr); - _name = name_; - _symbol = symbol_; - _storageURL = storageURL_; - _owner = msg.sender; - } - - function name() external view returns (string memory) { - return _name; - } - - function symbol() external view returns (string memory) { - return _symbol; - } - - function config() external view returns (string memory) { - return _storageURL; - } - - function setConfig(string memory storageURL) external { - require(msg.sender == _owner, "Not owner"); - _storageURL = storageURL; - } - - function tokenURI(uint256 tokenId) external view returns (string memory) { - require(_exists(tokenId), "Token does not exist"); - return string(abi.encodePacked(_storageURL)); - } - - function update( - uint256 tokenId, - bytes calldata proof - ) external { - TokenData storage token = _tokens[tokenId]; - require(token.owner == msg.sender, "Not owner"); - - OwnershipProofOutput memory proofOupt = _verifier.verifyOwnership(proof); - bytes32[] memory newDataHashes = proofOupt.dataHashes; - require( - proofOupt.isValid, - "Invalid ownership proof" - ); - - bytes32[] memory oldDataHashes = token.dataHashes; - token.dataHashes = newDataHashes; - - emit Updated(tokenId, oldDataHashes, newDataHashes); - } - - function dataHashesOf(uint256 tokenId) external view returns (bytes32[] memory) { - TokenData storage token = _tokens[tokenId]; - require(token.owner != address(0), "Token not exist"); - return token.dataHashes; - } - - function verifier() external view returns (IDataVerifier) { - return _verifier; - } - - function mint(bytes calldata proof) - external - payable - returns (uint256 tokenId) - { - OwnershipProofOutput memory proofOupt = _verifier.verifyOwnership(proof); - bytes32[] memory dataHashes = proofOupt.dataHashes; - require( - proofOupt.isValid, - "Invalid ownership proof" - ); - - tokenId = _nextTokenId++; - _tokens[tokenId] = TokenData({ - owner: msg.sender, - dataHashes: dataHashes, - authorizedUsers: new address[](0) - }); - - emit Minted(tokenId, msg.sender, dataHashes); - } - - function transfer( - address to, - uint256 tokenId, - bytes calldata proof - ) external { - require(to != address(0), "Zero address"); - - TokenData storage token = _tokens[tokenId]; - require(token.owner == msg.sender, "Not owner"); - - TransferValidityProofOutput memory proofOupt = _verifier.verifyTransferValidity(proof); - bytes32[] memory oldDataHashes = proofOupt.oldDataHashes; - bytes32[] memory newDataHashes = proofOupt.newDataHashes; - bytes32[] memory tokenDataHashes = token.dataHashes; - bytes memory pubKey = proofOupt.pubKey; - bytes memory sealedKey = proofOupt.sealedKey; - - require( - proofOupt.isValid - && _isEqual(oldDataHashes, tokenDataHashes) - && _pubKeyToAddress(pubKey) == to, - "Invalid transfer validity proof" - ); - - token.owner = to; - token.dataHashes = newDataHashes; - - emit Transferred(tokenId, msg.sender, to); - emit PublishedSealedKey(to, tokenId, sealedKey); - } - - function transferPublic( - address to, - uint256 tokenId - ) external { - require(to != address(0), "Zero address"); - require(_tokens[tokenId].owner == msg.sender, "Not owner"); - _tokens[tokenId].owner = to; - emit Transferred(tokenId, msg.sender, to); - } - - function clone( - address to, - uint256 tokenId, - bytes calldata proof - ) external payable returns (uint256) { - require(to != address(0), "Zero address"); - - require(_tokens[tokenId].owner == msg.sender, "Not owner"); - - TransferValidityProofOutput memory proofOupt = _verifier.verifyTransferValidity(proof); - bytes32[] memory oldDataHashes = proofOupt.oldDataHashes; - bytes32[] memory newDataHashes = proofOupt.newDataHashes; - bytes32[] memory tokenDataHashes = _tokens[tokenId].dataHashes; - bytes memory pubKey = proofOupt.pubKey; - bytes memory sealedKey = proofOupt.sealedKey; - - require( - proofOupt.isValid - && _isEqual(oldDataHashes, tokenDataHashes) - && _pubKeyToAddress(pubKey) == to, - "Invalid transfer validity proof" - ); - - uint256 newTokenId = _nextTokenId++; - _tokens[newTokenId] = TokenData({ - owner: to, - dataHashes: newDataHashes, - authorizedUsers: new address[](0) - }); - - emit Cloned(tokenId, newTokenId, msg.sender, to); - emit PublishedSealedKey(to, newTokenId, sealedKey); - return newTokenId; - } - - function clonePublic( - address to, - uint256 tokenId - ) external payable returns (uint256) { - require(to != address(0), "Zero address"); - require(_tokens[tokenId].owner == msg.sender, "Not owner"); - - uint256 newTokenId = _nextTokenId++; - _tokens[newTokenId] = TokenData({ - owner: to, - dataHashes: _tokens[tokenId].dataHashes, - authorizedUsers: new address[](0) - }); - emit Cloned(tokenId, newTokenId, msg.sender, to); - return newTokenId; - } - - function authorizeUsage(uint256 tokenId, address user) external { - require(_tokens[tokenId].owner == msg.sender, "Not owner"); - _tokens[tokenId].authorizedUsers.push(user); - emit AuthorizedUsage(tokenId, user); - } - - function ownerOf(uint256 tokenId) external view returns (address) { - TokenData storage token = _tokens[tokenId]; - require(token.owner != address(0), "Token not exist"); - return token.owner; - } - - function authorizedUsersOf(uint256 tokenId) external view returns (address[] memory) { - TokenData storage token = _tokens[tokenId]; - require(token.owner != address(0), "Token not exist"); - return token.authorizedUsers; - } -} -``` +For the reference implementation, please refer to 0G Labs' [0G Agent NFT](https://github.com/0glabs/0g-agent-nft/tree/eip-7844-draft). ## Security Considerations From 4bae812b3bca793fc79308e050b03ebfa3a29590 Mon Sep 17 00:00:00 2001 From: wei Date: Sat, 11 Jan 2025 11:37:16 +0800 Subject: [PATCH 13/34] modify implementation url --- ERCS/erc-7857.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 98ead754360..97507ed94f6 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -291,7 +291,7 @@ The design choices in this standard are motivated by several key requirements: This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721](./erc-721.md) if traditional NFT compatibility is desired. ## Reference Implementation -For the reference implementation, please refer to 0G Labs' [0G Agent NFT](https://github.com/0glabs/0g-agent-nft/tree/eip-7844-draft). +For the reference implementation, please refer to 0G Labs' [0G Agent NFT](https://github.com/0glabs/0g-agent-nft/tree/eip-7857-draft). ## Security Considerations From 271fbdba0ab14b30e35a69a3a4ec2f4c50d88148 Mon Sep 17 00:00:00 2001 From: Wei Wu <161988856+Wilbert957@users.noreply.github.com> Date: Thu, 30 Jan 2025 23:22:14 +0800 Subject: [PATCH 14/34] Update ERCS/erc-7857.md Co-authored-by: Mercy Boma Naps Nkari <96525594+bomanaps@users.noreply.github.com> --- ERCS/erc-7857.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 97507ed94f6..84193c77630 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -298,7 +298,7 @@ For the reference implementation, please refer to 0G Labs' [0G Agent NFT](https: 1. **Proof Verification** - Implementations must carefully verify all assertions in the proof - Replay attacks must be prevented - - Different verfication systems have their own security considerations, and distinct capabilities regarding key management: TEE can securely handle private keys from multi-parties, enabling direct data re-encryption. However, ZKP, due to its cryptographic nature, cannot process private keys from multi-parties. As a result, the re-encryption key is also from the prover (i.e., the sender), so tokens acquired through transfer or cloning must undergo re-encryption during their next update, otherwise the new update is still visible to the previous owner. This distinction in key handling capabilities affects how data transformations are managed during later usage + - Different verification systems have their own security considerations, and distinct capabilities regarding key management: TEE can securely handle private keys from multi-parties, enabling direct data re-encryption. However, ZKP, due to its cryptographic nature, cannot process private keys from multi-parties. As a result, the re-encryption key is also from the prover (i.e., the sender), so tokens acquired through transfer or cloning must undergo re-encryption during their next update, otherwise the new update is still visible to the previous owner. This distinction in key handling capabilities affects how data transformations are managed during later usage 2. **Data Privacy** - Only hashes and sealed keys are stored on-chain, actual functional data must be stored and transmitted securely off-chain - Key management is crucial for secure data access From 19bd1631f79e0a33ddde46e69dc32cf5f2347e28 Mon Sep 17 00:00:00 2001 From: Wei Wu <161988856+Wilbert957@users.noreply.github.com> Date: Thu, 30 Jan 2025 23:22:24 +0800 Subject: [PATCH 15/34] Update ERCS/erc-7857.md Co-authored-by: Mercy Boma Naps Nkari <96525594+bomanaps@users.noreply.github.com> --- ERCS/erc-7857.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 84193c77630..87fb7dfb4f4 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -303,7 +303,7 @@ For the reference implementation, please refer to 0G Labs' [0G Agent NFT](https: - Only hashes and sealed keys are stored on-chain, actual functional data must be stored and transmitted securely off-chain - Key management is crucial for secure data access - TEE verification system could support private key of the receiver, but ZKP verification system could not. So when using ZKP, the token transfered or cloned from other should be re-encrypted when next update, otherwise the new update is still visible to the previous owner -3. **Access Control and State Managementl** +3. **Access Control and State Management** - Operations restricted to token owners only - All data operations must maintain integrity and availability - Critical state changes (sealed keys, ownership, permissions) must be atomic and verifiable From 1fe7561276e1598db79fccc1df1033f4ff18584e Mon Sep 17 00:00:00 2001 From: Wei Wu <161988856+Wilbert957@users.noreply.github.com> Date: Thu, 30 Jan 2025 23:22:44 +0800 Subject: [PATCH 16/34] Update ERCS/erc-7857.md Co-authored-by: Mercy Boma Naps Nkari <96525594+bomanaps@users.noreply.github.com> --- ERCS/erc-7857.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 87fb7dfb4f4..858378889dc 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -274,7 +274,7 @@ function authorizedUsersOf(uint256 _tokenId) external view returns (address[] me The design choices in this standard are motivated by several key requirements: -1. **Verification Abstraction**: The standard separates the verification logic into a dedicated interface (IDataVerifier), allowing different verification mechanisms (TEE, ZKP) to be implemented and used interchangeably. The verfier should support two types of proof: +1. **Verification Abstraction**: The standard separates the verification logic into a dedicated interface (IDataVerifier), allowing different verification mechanisms (TEE, ZKP) to be implemented and used interchangeably. The verifier should support two types of proof: - Ownership Proof Verifies that the prover possesses the original data by demonstrating knowledge of the pre-images that generate the claimed dataHashes - Transfer Validity Proof Verifies secure data integrity and availability by proving: knowledge of the original data (pre-images of oldDataHashes); ability to decrypt with oldKey and re-encrypt with newKey; secure transmission of newKey using recipient's public key; integrity of the newly encrypted data matching newDataHashes; and data availability confirmed by recipient's signature on both oldDataHashes and newDataHashes 2. **Data Protection**: The standard uses data hashes and encrypted keys to ensure that valuable NFT data remains protected while still being integrity and availability verifiable From f4fa5d99e62540649551a0dec97f21f49ee39188 Mon Sep 17 00:00:00 2001 From: Wei Wu <161988856+Wilbert957@users.noreply.github.com> Date: Thu, 30 Jan 2025 23:22:56 +0800 Subject: [PATCH 17/34] Update ERCS/erc-7857.md Co-authored-by: Mercy Boma Naps Nkari <96525594+bomanaps@users.noreply.github.com> --- ERCS/erc-7857.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 858378889dc..33bd054ac76 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -302,7 +302,7 @@ For the reference implementation, please refer to 0G Labs' [0G Agent NFT](https: 2. **Data Privacy** - Only hashes and sealed keys are stored on-chain, actual functional data must be stored and transmitted securely off-chain - Key management is crucial for secure data access - - TEE verification system could support private key of the receiver, but ZKP verification system could not. So when using ZKP, the token transfered or cloned from other should be re-encrypted when next update, otherwise the new update is still visible to the previous owner + - TEE verification system could support private key of the receiver, but ZKP verification system could not. So when using ZKP, the token transferred or cloned from other should be re-encrypted when next update, otherwise the new update is still visible to the previous owner 3. **Access Control and State Management** - Operations restricted to token owners only - All data operations must maintain integrity and availability From f8a098123511cf6e800396859b474c87176eee08 Mon Sep 17 00:00:00 2001 From: Wei Wu <161988856+Wilbert957@users.noreply.github.com> Date: Mon, 24 Mar 2025 10:12:48 +0800 Subject: [PATCH 18/34] Update ERCS/erc-7857.md Co-authored-by: Sam Wilson <57262657+SamWilsn@users.noreply.github.com> --- ERCS/erc-7857.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 33bd054ac76..1a250ada186 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -73,7 +73,7 @@ The system supports two types of proofs: * Correct decryption and re-encryption of data * Secure key transmission (using receiver's public key to encrypt the new key) * Data availability in storage (using receiver's signature to confirm the data is available in storage) - - Verified on-chain through verifyTransferValidity() + - Verified on-chain through `verifyTransferValidity()` The ownership verification is optional because when the minted token is transferred or cloned, the ownership verification is checked again inside the availability verification. But for better safe than sorry, we recommend using ownership verification for minting and updates. From ca3d3b0c89123cea715a8d5e74c049050595aa33 Mon Sep 17 00:00:00 2001 From: Wei Wu <161988856+Wilbert957@users.noreply.github.com> Date: Mon, 24 Mar 2025 10:13:51 +0800 Subject: [PATCH 19/34] Update ERCS/erc-7857.md Co-authored-by: Sam Wilson <57262657+SamWilsn@users.noreply.github.com> --- ERCS/erc-7857.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 1a250ada186..2995982128c 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -75,7 +75,7 @@ The system supports two types of proofs: * Data availability in storage (using receiver's signature to confirm the data is available in storage) - Verified on-chain through `verifyTransferValidity()` -The ownership verification is optional because when the minted token is transferred or cloned, the ownership verification is checked again inside the availability verification. But for better safe than sorry, we recommend using ownership verification for minting and updates. +The ownership verification is optional because when the minted token is transferred or cloned, the ownership verification is checked again inside the availability verification. It's better to be safe than sorry, so we recommend doing ownership verification for minting and updates. Different verification mechanisms have distinct capabilities: From b2f0b936bf2e6341925427c8f239d0e62f036eb3 Mon Sep 17 00:00:00 2001 From: Wei Wu <161988856+Wilbert957@users.noreply.github.com> Date: Mon, 24 Mar 2025 10:14:07 +0800 Subject: [PATCH 20/34] Update ERCS/erc-7857.md Co-authored-by: Sam Wilson <57262657+SamWilsn@users.noreply.github.com> --- ERCS/erc-7857.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 2995982128c..26e3d5e9baa 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -274,7 +274,7 @@ function authorizedUsersOf(uint256 _tokenId) external view returns (address[] me The design choices in this standard are motivated by several key requirements: -1. **Verification Abstraction**: The standard separates the verification logic into a dedicated interface (IDataVerifier), allowing different verification mechanisms (TEE, ZKP) to be implemented and used interchangeably. The verifier should support two types of proof: +1. **Verification Abstraction**: The standard separates the verification logic into a dedicated interface (`IDataVerifier`), allowing different verification mechanisms (TEE, ZKP) to be implemented and used interchangeably. The verifier should support two types of proof: - Ownership Proof Verifies that the prover possesses the original data by demonstrating knowledge of the pre-images that generate the claimed dataHashes - Transfer Validity Proof Verifies secure data integrity and availability by proving: knowledge of the original data (pre-images of oldDataHashes); ability to decrypt with oldKey and re-encrypt with newKey; secure transmission of newKey using recipient's public key; integrity of the newly encrypted data matching newDataHashes; and data availability confirmed by recipient's signature on both oldDataHashes and newDataHashes 2. **Data Protection**: The standard uses data hashes and encrypted keys to ensure that valuable NFT data remains protected while still being integrity and availability verifiable From 6e45203e1e561504cefb3ac0692542248f8bf3f5 Mon Sep 17 00:00:00 2001 From: Wei Wu <161988856+Wilbert957@users.noreply.github.com> Date: Mon, 24 Mar 2025 10:14:47 +0800 Subject: [PATCH 21/34] Update ERCS/erc-7857.md Co-authored-by: Sam Wilson <57262657+SamWilsn@users.noreply.github.com> --- ERCS/erc-7857.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 26e3d5e9baa..abf28720ac6 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -276,7 +276,7 @@ The design choices in this standard are motivated by several key requirements: 1. **Verification Abstraction**: The standard separates the verification logic into a dedicated interface (`IDataVerifier`), allowing different verification mechanisms (TEE, ZKP) to be implemented and used interchangeably. The verifier should support two types of proof: - Ownership Proof Verifies that the prover possesses the original data by demonstrating knowledge of the pre-images that generate the claimed dataHashes - - Transfer Validity Proof Verifies secure data integrity and availability by proving: knowledge of the original data (pre-images of oldDataHashes); ability to decrypt with oldKey and re-encrypt with newKey; secure transmission of newKey using recipient's public key; integrity of the newly encrypted data matching newDataHashes; and data availability confirmed by recipient's signature on both oldDataHashes and newDataHashes + - Transfer Validity Proof Verifies secure data integrity and availability by proving: knowledge of the original data (pre-images of oldDataHashes); ability to decrypt with oldKey and re-encrypt with newKey; secure transmission of newKey using recipient's public key; integrity of the newly encrypted data matching newDataHashes; and data availability confirmed by recipient's signature on both `oldDataHashes` and `newDataHashes` 2. **Data Protection**: The standard uses data hashes and encrypted keys to ensure that valuable NFT data remains protected while still being integrity and availability verifiable 3. **Flexible Data Management**: Three distinct data operations are supported: From 3819c44b59fea447b6e34b3b92afc311def62303 Mon Sep 17 00:00:00 2001 From: wei Date: Mon, 24 Mar 2025 10:53:17 +0800 Subject: [PATCH 22/34] fix: address content and formatting issues - Convert direct URL to reference-style link format - Add reference link definition at document bottom - Fix internal links to ERC-721 standard - Expand abbreviations (TEE, ZKP) on first use - Clarify the abstract that the NFTs represent the agent itself --- ERCS/erc-7857.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index abf28720ac6..52709fe5eda 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -12,7 +12,7 @@ created: 2025-01-02 ## Abstract -A standard interface for NFTs specifically designed for AI agents, where the metadata represents agent capabilities and requires privacy protection. Unlike traditional NFT standards that focus on static metadata, this standard introduces mechanisms for verifiable data ownership and secure transfer. By defining a unified interface for different verification methods (e.g., TEE, ZKP), it enables secure management of valuable agent metadata such as models, memory, and character definitions, while maintaining confidentiality and verifiability. +A standard interface for NFTs specifically designed for AI agents, where the metadata represents agent capabilities and requires privacy protection. Unlike traditional NFT standards that focus on static metadata, this standard introduces mechanisms for verifiable data ownership and secure transfer. By defining a unified interface for different verification methods (e.g., Trusted Execution Environment (TEE), Zero-Knowledge Proof (ZKP)), it enables secure management of valuable agent metadata such as models, memory, and character definitions, while maintaining confidentiality and verifiability. ## Motivation @@ -24,7 +24,7 @@ AI agents possess inherent non-fungible properties that make them natural candid 2. Agents embody clear ownership rights, representing significant computational investment and intellectual property 3. Agents have private metadata (e.g., neural network models, memory, character definitions) that defines their capabilities -However, current NFT standards like [ERC-721](./erc-721.md) are insufficient for representing AI agents effectively. The key challenge lies in the metadata transfer mechanism. Unlike traditional NFTs where metadata is typically static and publicly accessible, an agent's metadata: +However, current NFT standards like [ERC-721][erc721] are insufficient for representing AI agents as digital assets. While NFTs can establish ownership of digital items, using them to represent AI agents introduces unique challenges. The key issue lies in the metadata transfer mechanism. Unlike traditional NFTs where metadata is typically static and publicly accessible, an AI agent's metadata (which constitutes the agent itself): 1. Has intrinsic value and is often the primary purpose of the transfer 2. Requires encrypted storage to protect intellectual property @@ -32,8 +32,8 @@ However, current NFT standards like [ERC-721](./erc-721.md) are insufficient for For example, when transferring an agent NFT, we need to ensure: -1. The actual transfer of encrypted metadata is verifiable -2. The new owner can securely access the metadata +1. The actual transfer of encrypted metadata (the agent's model, memory, character, etc.) is verifiable +2. The new owner can securely access the metadata that constitutes the agent 3. The agent's execution environment can verify ownership and load appropriate metadata This EIP introduces a standard for NFTs with private metadata that addresses these requirements through privacy-preserving verification mechanisms, enabling secure ownership and transfer of valuable agent data while maintaining confidentiality and verifiability. This standard will serve as a foundation for the emerging agent ecosystem, allowing platforms to provide verifiable agent ownership and secure metadata management in a decentralized manner. @@ -288,10 +288,10 @@ The design choices in this standard are motivated by several key requirements: ## Backwards Compatibility -This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721](./erc-721.md) if traditional NFT compatibility is desired. +This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721][erc721] if traditional NFT compatibility is desired. ## Reference Implementation -For the reference implementation, please refer to 0G Labs' [0G Agent NFT](https://github.com/0glabs/0g-agent-nft/tree/eip-7857-draft). +For the reference implementation, please refer to 0G Labs' [0G Agent NFT][0g-agent-nft]. ## Security Considerations @@ -315,4 +315,8 @@ For the reference implementation, please refer to 0G Labs' [0G Agent NFT](https: ## Copyright -Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file +Copyright and related rights waived via [CC0](../LICENSE.md). + + +[0g-agent-nft]: https://github.com/0glabs/0g-agent-nft/tree/eip-7857-draft +[erc721]: ./erc-721.md \ No newline at end of file From 5e184c828d35b517474876816c454d16ab3095d6 Mon Sep 17 00:00:00 2001 From: wei Date: Mon, 24 Mar 2025 11:24:36 +0800 Subject: [PATCH 23/34] fix: formatting issues 1. Modify `erc-721` to `eip-721` 2. Move the external link to `Reference` --- ERCS/erc-7857.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 52709fe5eda..8be82748f1a 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -291,7 +291,7 @@ The design choices in this standard are motivated by several key requirements: This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721][erc721] if traditional NFT compatibility is desired. ## Reference Implementation -For the reference implementation, please refer to 0G Labs' [0G Agent NFT][0g-agent-nft]. +For the reference implementation, please refer to 0G Labs' 0G Agent NFT[1]. ## Security Considerations @@ -317,6 +317,8 @@ For the reference implementation, please refer to 0G Labs' [0G Agent NFT][0g-age Copyright and related rights waived via [CC0](../LICENSE.md). +## References -[0g-agent-nft]: https://github.com/0glabs/0g-agent-nft/tree/eip-7857-draft -[erc721]: ./erc-721.md \ No newline at end of file +[1] 0G Labs. "0G Agent NFT". GitHub. https://github.com/0glabs/0g-agent-nft/tree/eip-7857-draft + +[erc721]: ./eip-721.md \ No newline at end of file From 205e9d22a8437e6be692a4914949dcd8c0d20f9a Mon Sep 17 00:00:00 2001 From: wei Date: Mon, 24 Mar 2025 11:29:39 +0800 Subject: [PATCH 24/34] fix: formatting issues 1. Remove the external link --- ERCS/erc-7857.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 8be82748f1a..98af898b5a1 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -291,7 +291,7 @@ The design choices in this standard are motivated by several key requirements: This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721][erc721] if traditional NFT compatibility is desired. ## Reference Implementation -For the reference implementation, please refer to 0G Labs' 0G Agent NFT[1]. +For the reference implementation, please refer to 0G Labs' 0G Agent NFT implementation (available in the 0glabs GitHub repository under the eip-7857-draft branch). ## Security Considerations @@ -317,8 +317,4 @@ For the reference implementation, please refer to 0G Labs' 0G Agent NFT[1]. Copyright and related rights waived via [CC0](../LICENSE.md). -## References - -[1] 0G Labs. "0G Agent NFT". GitHub. https://github.com/0glabs/0g-agent-nft/tree/eip-7857-draft - [erc721]: ./eip-721.md \ No newline at end of file From 408a3476948828b801418cf7a344afb04217bc34 Mon Sep 17 00:00:00 2001 From: wei Date: Mon, 24 Mar 2025 11:35:53 +0800 Subject: [PATCH 25/34] fix: formatting issues 1. place the repo name and the branch name in backticks --- ERCS/erc-7857.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 98af898b5a1..3a157ebb6bc 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -291,7 +291,7 @@ The design choices in this standard are motivated by several key requirements: This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721][erc721] if traditional NFT compatibility is desired. ## Reference Implementation -For the reference implementation, please refer to 0G Labs' 0G Agent NFT implementation (available in the 0glabs GitHub repository under the eip-7857-draft branch). +For the reference implementation, please refer to 0G Labs' 0G Agent NFT implementation (available in the 0glabs GitHub repository `0g-agent-nft` under the `eip-7857-draft` branch). ## Security Considerations From f4116d1bf7c3343ed0629006aab0fe4f0b19a657 Mon Sep 17 00:00:00 2001 From: wei Date: Mon, 24 Mar 2025 11:38:05 +0800 Subject: [PATCH 26/34] fix: formatting issues 1. remove the branch name --- ERCS/erc-7857.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 3a157ebb6bc..3bf7acc36fa 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -291,7 +291,7 @@ The design choices in this standard are motivated by several key requirements: This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721][erc721] if traditional NFT compatibility is desired. ## Reference Implementation -For the reference implementation, please refer to 0G Labs' 0G Agent NFT implementation (available in the 0glabs GitHub repository `0g-agent-nft` under the `eip-7857-draft` branch). +For the reference implementation, please refer to 0G Labs' 0G Agent NFT implementation (available in the 0glabs GitHub repository `0g-agent-nft`). ## Security Considerations From 874943bebdf847226b0ab2738311e42aeb8945d6 Mon Sep 17 00:00:00 2001 From: wei Date: Mon, 11 Aug 2025 11:35:31 +0800 Subject: [PATCH 27/34] ERC-7857: change status from Draft to Review - Updated status from Draft to Review - Added review-period-end date (45 days from now) - EIP is ready for community peer review --- ERCS/erc-7857.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 3bf7acc36fa..6242a3ff1f1 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -4,7 +4,8 @@ title: AI Agents NFT with Private Metadata description: AI agent NFTs, enabling secure management of private metadata with verifiable data transfer author: Ming Wu (@sparkmiw), Jason Zeng (@zenghbo), Wei Wu (@Wilbert957), Michael Heinrich (@michaelomg) discussions-to: https://ethereum-magicians.org/t/erc-7857-an-nft-standard-for-ai-agents-with-private-metadata/22391 -status: Draft +status: Review +review-period-end: 2025-09-25 type: Standards Track category: ERC created: 2025-01-02 From e85e6306d25d0f337d962838880a9bbd49ae5e24 Mon Sep 17 00:00:00 2001 From: wei Date: Mon, 11 Aug 2025 11:40:23 +0800 Subject: [PATCH 28/34] remove `review-period-end` field --- ERCS/erc-7857.md | 1 - 1 file changed, 1 deletion(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 6242a3ff1f1..6c6f4ffd563 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -5,7 +5,6 @@ description: AI agent NFTs, enabling secure management of private metadata with author: Ming Wu (@sparkmiw), Jason Zeng (@zenghbo), Wei Wu (@Wilbert957), Michael Heinrich (@michaelomg) discussions-to: https://ethereum-magicians.org/t/erc-7857-an-nft-standard-for-ai-agents-with-private-metadata/22391 status: Review -review-period-end: 2025-09-25 type: Standards Track category: ERC created: 2025-01-02 From a6a78e39fdbc77befbe7a18fb03ea10e13ca3ff6 Mon Sep 17 00:00:00 2001 From: wei Date: Thu, 4 Sep 2025 10:51:44 +0800 Subject: [PATCH 29/34] remove external reference implementation --- ERCS/erc-7857.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 6c6f4ffd563..ece400c3479 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -290,9 +290,6 @@ The design choices in this standard are motivated by several key requirements: This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721][erc721] if traditional NFT compatibility is desired. -## Reference Implementation -For the reference implementation, please refer to 0G Labs' 0G Agent NFT implementation (available in the 0glabs GitHub repository `0g-agent-nft`). - ## Security Considerations 1. **Proof Verification** From 4a38df842a953e555163cceaa099330ecb315bf3 Mon Sep 17 00:00:00 2001 From: wei Date: Sun, 7 Sep 2025 18:04:24 +0800 Subject: [PATCH 30/34] 1. rename some interface 2. add some events 3. add reference implementation --- ERCS/erc-7857.md | 1122 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 964 insertions(+), 158 deletions(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index ece400c3479..59ef3d3aa7d 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -94,180 +94,256 @@ Different verification mechanisms have distinct capabilities: ### Data Verification Interface ```solidity -/// @notice Output of ownership proof verification -struct OwnershipProofOutput { - bytes32[] dataHashes; - bool isValid; +/// @notice The type of the oracle +/// There are two types of oracles: TEE and ZKP +enum OracleType { + TEE, + ZKP +} + +/// @notice The access proof which is a signature signed by the receiver (the receiver may delegate the signing privilege to the access assistant) +/// @param oldDataHash The hash of the old data +/// @param newDataHash The hash of the new data +/// @param nonce The nonce of the access proof +/// @param encryptedPubKey The encrypted public key, the receiver's public key which used to encrypt the new data key. `encryptedPubKey` can be empty in `accessProof`, and means that use the receiver's ethereum public key to encrypt the new data key +/// @param proof The proof +struct AccessProof { + bytes32 oldDataHash; + bytes32 newDataHash; + bytes nonce; + bytes encryptedPubKey; + bytes proof; +} + +/// @notice The ownership proof which is a signature signed by the receiver (the receiver may delegate the signing privilege to the access assistant) +/// @param proofType The type of the proof +/// @param oldDataHash The hash of the old data +/// @param newDataHash The hash of the new data +/// @param sealedKey The sealed key of the new data key +/// @param encryptedPubKey The encrypted public key, the receiver's public key which used to encrypt the new data key +/// @param nonce The nonce +struct OwnershipProof { + OracleType oracleType; // The type of the oracle + bytes32 oldDataHash; // The hash of the old data + bytes32 newDataHash; // The hash of the new data + bytes sealedKey; // The sealed key of the new data key + bytes encryptedPubKey; // The encrypted public key, the receiver's public key which used to encrypt the new data key + bytes nonce; // The nonce + bytes proof; // The proof +} + +struct TransferValidityProof { + AccessProof accessProof; + OwnershipProof ownershipProof; } -/// @notice Output of transfer validity proof verification struct TransferValidityProofOutput { - bytes32[] oldDataHashes; - bytes32[] newDataHashes; - bytes pubKey; + bytes32 oldDataHash; + bytes32 newDataHash; bytes sealedKey; - bool isValid; + bytes encryptedPubKey; + bytes wantedKey; + address accessAssistant; + bytes accessProofNonce; + bytes ownershipProofNonce; } -/// @notice Verify ownership of data -/// @param _proof Proof generated by companion prover system -/// @return Verification result and validated data hashes -function verifyOwnership( - bytes calldata _proof -) external view returns (OwnershipProofOutput memory); - -/// @notice Verify data transfer validity -/// @param _proof Proof generated by companion prover system -/// @return Verification result and transfer details -function verifyTransferValidity( - bytes calldata _proof -) external view returns (TransferValidityProofOutput memory); +interface IERC7857DataVerifier { + /// @notice Verify data transfer validity, the _proofs prove: + /// 1. The pre-image of oldDataHashes + /// 2. The oldKey (old data key) can decrypt the pre-image and the new key re-encrypt the plaintexts to new ciphertexts + /// 3. The newKey (new data key) is encrypted using the encryptedPubKey + /// 4. The hashes of new ciphertexts is newDataHashes + /// 5. The newDataHashes identified ciphertexts are available in the storage: need the signature from the receiver or the access assistant signing oldDataHashes, newDataHashes, and encryptedPubKey + /// @param _proofs Proof generated by TEE/ZKP + function verifyTransferValidity( + TransferValidityProof[] calldata _proofs + ) external returns (TransferValidityProofOutput[] memory); +} ``` ### Metadata Interface ```solidity -/// @dev This emits when data is updated -event Updated( - uint256 indexed _tokenId, - bytes32[] _oldDataHashes, - bytes32[] _newDataHashes -); - -/// @notice Get the name of the NFT collection -function name() external view returns (string memory); - -/// @notice Get the symbol of the NFT collection -function symbol() external view returns (string memory); - -/// @notice Get the metadata URI for a specific token -function tokenURI(uint256 tokenId) external view returns (string memory); - -/// @notice Update data -/// @param _tokenId The token to update -/// @param _proofs Proof of updated data ownership -function update( - uint256 _tokenId, - bytes[] calldata _proofs -) external; - -/// @notice Get the data hash of a token -/// @param _tokenId The token identifier -/// @return The current data hash of the token -function dataHashesOf(uint256 _tokenId) public view returns (bytes32[] memory); - -/// @notice Get the data description of a token -/// @param _tokenId The token identifier -/// @return The current data description of the token -function dataDescriptionsOf(uint256 _tokenId) public view returns (string[] memory); +struct IntelligentData { + string dataDescription; + bytes32 dataHash; +} + +interface IERC7857Metadata { + /// @notice Get the name of the NFT collection + function name() external view returns (string memory); + + /// @notice Get the symbol of the NFT collection + function symbol() external view returns (string memory); + + /// @notice Get the data hash of a token + /// @param _tokenId The token identifier + /// @return The current data hash of the token + function intelligentDatasOf(uint256 _tokenId) external view returns (IntelligentData[] memory); +} ``` ### Main NFT Interface ```solidity -/// @dev This emits when a new functional NFT is minted -event Minted( - uint256 indexed _tokenId, - address indexed _creator, - bytes32[] _dataHashes, - string[] _dataDescriptions -); - -/// @dev This emits when a user is authorized to use the data -event AuthorizedUsage( - uint256 indexed _tokenId, - address indexed _user -); - -/// @dev This emits when data is transferred with ownership -event Transferred( - uint256 _tokenId, - address indexed _from, - address indexed _to -); - -/// @dev This emits when data is cloned -event Cloned( - uint256 indexed _tokenId, - uint256 indexed _newTokenId, - address _from, - address _to -); - -/// @dev This emits when a sealed key is published -event PublishedSealedKey( - address indexed _to, - uint256 indexed _tokenId, - bytes _sealedKey -); - -/// @notice The verifier interface that this NFT uses -/// @return The address of the verifier contract -function verifier() external view returns (IDataVerifier); - -/// @notice Mint new NFT with data ownership proof -/// @param _proofs Proofs of data ownership -/// @param _dataDescriptions Descriptions of the data -/// @return _tokenId The ID of the newly minted token -function mint(bytes[] calldata _proofs, string[] calldata _dataDescriptions) - external - payable - returns (uint256 _tokenId); - -/// @notice Transfer full data (full means data and ownership) -/// @param _to Address to transfer data to -/// @param _tokenId The token to transfer data for -/// @param _proofs Proofs of data available for _to -function transfer( - address _to, - uint256 _tokenId, - bytes[] calldata _proofs -) external; - -/// @notice Clone data (clone means just data, not ownership) -/// @param _to Address to clone data to -/// @param _tokenId The token to clone data for -/// @param _proofs Proofs of data available for _to -/// @return _newTokenId The ID of the newly cloned token -function clone( - address _to, - uint256 _tokenId, - bytes[] calldata _proofs -) external payable returns (uint256 _newTokenId); - -/// @notice Transfer public data with ownership -/// @param _to Address to transfer data to -/// @param _tokenId The token to transfer data for -function transferPublic( - address _to, - uint256 _tokenId -) external; - -/// @notice Clone public data -/// @param _to Address to clone data to -/// @param _tokenId The token to clone data for -/// @return _newTokenId The ID of the newly cloned token -function clonePublic( - address _to, - uint256 _tokenId -) external payable returns (uint256 _newTokenId); - -/// @notice Add authorized user to group -/// @param _tokenId The token to add to group -function authorizeUsage( - uint256 _tokenId, - address _user -) external; - -/// @notice Get token owner -/// @param _tokenId The token identifier -/// @return The current owner of the token -function ownerOf(uint256 _tokenId) external view returns (address); - -/// @notice Get the authorized users of a token -/// @param _tokenId The token identifier -/// @return The current authorized users of the token -function authorizedUsersOf(uint256 _tokenId) external view returns (address[] memory); +interface IERC7857 { + /// @notice The event emitted when an address is approved to transfer a token + /// @param _from The address that is approving + /// @param _to The address that is being approved + /// @param _tokenId The token identifier + event Approval( + address indexed _from, + address indexed _to, + uint256 indexed _tokenId + ); + + /// @notice The event emitted when an address is approved for all + /// @param _owner The owner + /// @param _operator The operator + /// @param _approved The approval + event ApprovalForAll( + address indexed _owner, + address indexed _operator, + bool _approved + ); + + /// @notice The event emitted when an address is authorized to use a token + /// @param _from The address that is authorizing + /// @param _to The address that is being authorized + /// @param _tokenId The token identifier + event Authorization( + address indexed _from, + address indexed _to, + uint256 indexed _tokenId + ); + + /// @notice The event emitted when an address is revoked from using a token + /// @param _from The address that is revoking + /// @param _to The address that is being revoked + /// @param _tokenId The token identifier + event AuthorizationRevoked( + address indexed _from, + address indexed _to, + uint256 indexed _tokenId + ); + + /// @notice The event emitted when a token is transferred + /// @param _tokenId The token identifier + /// @param _from The address that is transferring + /// @param _to The address that is receiving + event Transferred( + uint256 _tokenId, + address indexed _from, + address indexed _to + ); + + /// @notice The event emitted when a token is cloned + /// @param _tokenId The token identifier + /// @param _newTokenId The new token identifier + /// @param _from The address that is cloning + /// @param _to The address that is receiving + event Cloned( + uint256 indexed _tokenId, + uint256 indexed _newTokenId, + address _from, + address _to + ); + + /// @notice The event emitted when a sealed key is published + /// @param _to The address that is receiving + /// @param _tokenId The token identifier + /// @param _sealedKeys The sealed keys + event PublishedSealedKey( + address indexed _to, + uint256 indexed _tokenId, + bytes[] _sealedKeys + ); + + /// @notice The event emitted when a user is delegated to an assistant + /// @param _user The user + /// @param _assistant The assistant + event DelegateAccess(address indexed _user, address indexed _assistant); + + /// @notice The verifier interface that this NFT uses + /// @return The address of the verifier contract + function verifier() external view returns (IERC7857DataVerifier); + + /// @notice Transfer data with ownership + /// @param _to Address to transfer data to + /// @param _tokenId The token to transfer data for + /// @param _proofs Proofs of data available for _to + function iTransfer( + address _to, + uint256 _tokenId, + TransferValidityProof[] calldata _proofs + ) external; + + /// @notice Clone data + /// @param _to Address to clone data to + /// @param _tokenId The token to clone data for + /// @param _proofs Proofs of data available for _to + /// @return _newTokenId The ID of the newly cloned token + function iClone( + address _to, + uint256 _tokenId, + TransferValidityProof[] calldata _proofs + ) external returns (uint256 _newTokenId); + + /// @notice Add authorized user to group + /// @param _tokenId The token to add to group + function authorizeUsage(uint256 _tokenId, address _user) external; + + /// @notice Revoke authorization from a user + /// @param _tokenId The token to revoke authorization from + /// @param _user The user to revoke authorization from + function revokeAuthorization(uint256 _tokenId, address _user) external; + + /// @notice Approve an address to transfer a token + /// @param _to The address to approve + /// @param _tokenId The token identifier + function approve(address _to, uint256 _tokenId) external; + + /// @notice Set approval for all + /// @param _operator The operator + /// @param _approved The approval + function setApprovalForAll(address _operator, bool _approved) external; + + /// @notice Delegate access check to an assistant + /// @param _assistant The assistant + function delegateAccess(address _assistant) external; + + /// @notice Get token owner + /// @param _tokenId The token identifier + /// @return The current owner of the token + function ownerOf(uint256 _tokenId) external view returns (address); + + /// @notice Get the authorized users of a token + /// @param _tokenId The token identifier + /// @return The current authorized users of the token + function authorizedUsersOf( + uint256 _tokenId + ) external view returns (address[] memory); + + /// @notice Get the approved address for a token + /// @param _tokenId The token identifier + /// @return The approved address + function getApproved(uint256 _tokenId) external view returns (address); + + /// @notice Check if an address is approved for all + /// @param _owner The owner + /// @param _operator The operator + /// @return The approval + function isApprovedForAll( + address _owner, + address _operator + ) external view returns (bool); + + /// @notice Get the delegate access for a user + /// @param _user The user + /// @return The delegate access + function getDelegateAccess(address _user) external view returns (address); +} ``` ## Rationale @@ -290,6 +366,736 @@ The design choices in this standard are motivated by several key requirements: This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721][erc721] if traditional NFT compatibility is desired. +## Reference Implementation +### Verifier +```solidity +abstract contract BaseVerifier is IERC7857DataVerifier { + // prevent replay attack + mapping(bytes32 => bool) internal usedProofs; + + // prevent replay attack + mapping(bytes32 => uint256) internal proofTimestamps; + + function _checkAndMarkProof(bytes32 proofNonce) internal { + require(!usedProofs[proofNonce], "Proof already used"); + usedProofs[proofNonce] = true; + proofTimestamps[proofNonce] = block.timestamp; + } + + // clean expired proof records (save gas) + function cleanExpiredProofs(bytes32[] calldata proofNonces) external { + for (uint256 i = 0; i < proofNonces.length; i++) { + bytes32 nonce = proofNonces[i]; + if (usedProofs[nonce] && + block.timestamp > proofTimestamps[nonce] + 7 days) { + delete usedProofs[nonce]; + delete proofTimestamps[nonce]; + } + } + } + + uint256[50] private __gap; +} + +struct AttestationConfig { + OracleType oracleType; + address contractAddress; +} + +contract Verifier is + BaseVerifier, + Initializable, + AccessControlUpgradeable, + PausableUpgradeable +{ + using ECDSA for bytes32; + using MessageHashUtils for bytes32; + + event AttestationContractUpdated(AttestationConfig[] attestationConfigs); + + bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); + bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + + mapping(OracleType => address) public attestationContract; + + uint256 public maxProofAge; + + string public constant VERSION = "2.0.0"; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize( + AttestationConfig[] calldata _attestationConfigs, + address _admin + ) external initializer { + __AccessControl_init(); + __Pausable_init(); + + for (uint256 i = 0; i < _attestationConfigs.length; i++) { + attestationContract[ + _attestationConfigs[i].oracleType + ] = _attestationConfigs[i].contractAddress; + } + maxProofAge = 7 days; + + _grantRole(DEFAULT_ADMIN_ROLE, _admin); + _grantRole(ADMIN_ROLE, _admin); + _grantRole(PAUSER_ROLE, _admin); + + emit AttestationContractUpdated(_attestationConfigs); + } + + function updateAttestationContract( + AttestationConfig[] calldata _attestationConfigs + ) external onlyRole(ADMIN_ROLE) { + for (uint256 i = 0; i < _attestationConfigs.length; i++) { + attestationContract[ + _attestationConfigs[i].oracleType + ] = _attestationConfigs[i].contractAddress; + } + + emit AttestationContractUpdated(_attestationConfigs); + } + + function updateMaxProofAge( + uint256 _maxProofAge + ) external onlyRole(ADMIN_ROLE) { + maxProofAge = _maxProofAge; + } + + function pause() external onlyRole(PAUSER_ROLE) { + _pause(); + } + + function unpause() external onlyRole(PAUSER_ROLE) { + _unpause(); + } + + function hashNonce(bytes memory nonce) private pure returns (bytes32) { + return keccak256(nonce); + } + + function teeOracleVerify( + bytes32 messageHash, + bytes memory signature + ) internal view returns (bool) { + return + TEEVerifier(attestationContract[OracleType.TEE]).verifyTEESignature( + messageHash, + signature + ); + } + + /// @notice Extract and verify signature from the access proof + /// @param accessProof The access proof + /// @return The recovered access assistant address + function verifyAccessibility( + AccessProof memory accessProof + ) private pure returns (address) { + bytes32 messageHash = keccak256( + abi.encodePacked( + "\x19Ethereum Signed Message:\n66", + Strings.toHexString( + uint256( + keccak256( + abi.encodePacked( + accessProof.oldDataHash, + accessProof.newDataHash, + accessProof.encryptedPubKey, + accessProof.nonce + ) + ) + ), + 32 + ) + ) + ); + + address accessAssistant = messageHash.recover(accessProof.proof); + require(accessAssistant != address(0), "Invalid access assistant"); + return accessAssistant; + } + + function verfifyOwnershipProof( + OwnershipProof memory ownershipProof + ) private view returns (bool) { + if (ownershipProof.oracleType == OracleType.TEE) { + bytes32 messageHash = keccak256( + abi.encodePacked( + "\x19Ethereum Signed Message:\n66", + Strings.toHexString( + uint256( + keccak256( + abi.encodePacked( + ownershipProof.oldDataHash, + ownershipProof.newDataHash, + ownershipProof.sealedKey, + ownershipProof.encryptedPubKey, + ownershipProof.nonce + ) + ) + ), + 32 + ) + ) + ); + + return teeOracleVerify(messageHash, ownershipProof.proof); + } + // TODO: add ZKP verification + else { + return false; + } + } + + /// @notice Process a single transfer validity proof + /// @param proof The proof data + /// @return output The processed proof data as a struct + function processTransferProof( + TransferValidityProof calldata proof + ) private view returns (TransferValidityProofOutput memory output) { + // compare the proof data in access proof and ownership proof + require( + proof.accessProof.oldDataHash == proof.ownershipProof.oldDataHash, + "Invalid oldDataHashes" + ); + output.oldDataHash = proof.accessProof.oldDataHash; + require( + proof.accessProof.newDataHash == proof.ownershipProof.newDataHash, + "Invalid newDataHashes" + ); + output.newDataHash = proof.accessProof.newDataHash; + + output.wantedKey = proof.accessProof.encryptedPubKey; + output.accessProofNonce = proof.accessProof.nonce; + output.encryptedPubKey = proof.ownershipProof.encryptedPubKey; + output.sealedKey = proof.ownershipProof.sealedKey; + output.ownershipProofNonce = proof.ownershipProof.nonce; + + // verify the access assistant + output.accessAssistant = verifyAccessibility(proof.accessProof); + + bool isOwn = verfifyOwnershipProof(proof.ownershipProof); + + require(isOwn, "Invalid ownership proof"); + + return output; + } + + /// @notice Verify data transfer validity, the _proof prove: + /// 1. The pre-image of oldDataHashes + /// 2. The oldKey can decrypt the pre-image and the new key re-encrypt the plaintexts to new ciphertexts + /// 3. The newKey is encrypted with the receiver's pubKey to get the sealedKey + /// 4. The hashes of new ciphertexts is newDataHashes (key to note: TEE could support a private key of the receiver) + /// 5. The newDataHashes identified ciphertexts are available in the storage: need the signature from the receiver signing oldDataHashes and newDataHashes + /// @param proofs Proof generated by TEE/ZKP oracle + function verifyTransferValidity( + TransferValidityProof[] calldata proofs + ) + public + virtual + override + whenNotPaused + returns (TransferValidityProofOutput[] memory) + { + TransferValidityProofOutput[] + memory outputs = new TransferValidityProofOutput[](proofs.length); + + for (uint256 i = 0; i < proofs.length; i++) { + TransferValidityProofOutput memory output = processTransferProof( + proofs[i] + ); + + outputs[i] = output; + + bytes32 accessProofNonce = hashNonce(output.accessProofNonce); + _checkAndMarkProof(accessProofNonce); + + bytes32 ownershipProofNonce = hashNonce(output.ownershipProofNonce); + _checkAndMarkProof(ownershipProofNonce); + } + + return outputs; + } + + uint256[50] private __gap; +} +``` +### Main NFT +```solidity +contract AgentNFT is + AccessControlEnumerableUpgradeable, + IERC7857, + IERC7857Metadata +{ + event Updated( + uint256 indexed _tokenId, + IntelligentData[] _oldDatas, + IntelligentData[] _newDatas + ); + + event Minted( + uint256 indexed _tokenId, + address indexed _creator, + address indexed _owner + ); + + struct TokenData { + address owner; + address[] authorizedUsers; + address approvedUser; + IntelligentData[] iDatas; + } + + /// @custom:storage-location erc7201:agent.storage.AgentNFT + struct AgentNFTStorage { + // Token data + mapping(uint256 => TokenData) tokens; + mapping(address owner => mapping(address operator => bool)) operatorApprovals; + mapping(address user => address accessAssistant) accessAssistants; + uint256 nextTokenId; + // Contract metadata + string name; + string symbol; + string storageInfo; + // Core components + IERC7857DataVerifier verifier; + } + + bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); + bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + + string public constant VERSION = "2.0.0"; + + // keccak256(abi.encode(uint(keccak256("agent.storage.AgentNFT")) - 1)) & ~bytes32(uint(0xff)) + bytes32 private constant AGENT_NFT_STORAGE_LOCATION = + 0x4aa80aaafbe0e5fe3fe1aa97f3c1f8c65d61f96ef1aab2b448154f4e07594600; + + function _getAgentStorage() + private + pure + returns (AgentNFTStorage storage $) + { + assembly { + $.slot := AGENT_NFT_STORAGE_LOCATION + } + } + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize( + string memory name_, + string memory symbol_, + string memory storageInfo_, + address verifierAddr, + address admin_ + ) public virtual initializer { + require(verifierAddr != address(0), "Zero address"); + + __AccessControlEnumerable_init(); + + _grantRole(DEFAULT_ADMIN_ROLE, admin_); + _grantRole(ADMIN_ROLE, admin_); + _grantRole(PAUSER_ROLE, admin_); + + AgentNFTStorage storage $ = _getAgentStorage(); + $.name = name_; + $.symbol = symbol_; + $.storageInfo = storageInfo_; + $.verifier = IERC7857DataVerifier(verifierAddr); + } + + // Basic getters + function name() public view virtual returns (string memory) { + return _getAgentStorage().name; + } + + function symbol() public view virtual returns (string memory) { + return _getAgentStorage().symbol; + } + + function verifier() public view virtual returns (IERC7857DataVerifier) { + return _getAgentStorage().verifier; + } + + // Admin functions + function updateVerifier( + address newVerifier + ) public virtual onlyRole(ADMIN_ROLE) { + require(newVerifier != address(0), "Zero address"); + _getAgentStorage().verifier = IERC7857DataVerifier(newVerifier); + } + + function update( + uint256 tokenId, + IntelligentData[] calldata newDatas + ) public virtual { + AgentNFTStorage storage $ = _getAgentStorage(); + TokenData storage token = $.tokens[tokenId]; + require(token.owner == msg.sender, "Not owner"); + require(newDatas.length > 0, "Empty data array"); + + IntelligentData[] memory oldDatas = new IntelligentData[]( + token.iDatas.length + ); + for (uint i = 0; i < token.iDatas.length; i++) { + oldDatas[i] = token.iDatas[i]; + } + + delete token.iDatas; + + for (uint i = 0; i < newDatas.length; i++) { + token.iDatas.push(newDatas[i]); + } + + emit Updated(tokenId, oldDatas, newDatas); + } + + function mint( + IntelligentData[] calldata iDatas, + address to + ) public payable virtual returns (uint256 tokenId) { + require(to != address(0), "Zero address"); + require(iDatas.length > 0, "Empty data array"); + + AgentNFTStorage storage $ = _getAgentStorage(); + + tokenId = $.nextTokenId++; + TokenData storage newToken = $.tokens[tokenId]; + newToken.owner = to; + newToken.approvedUser = address(0); + + for (uint i = 0; i < iDatas.length; i++) { + newToken.iDatas.push(iDatas[i]); + } + + emit Minted(tokenId, msg.sender, to); + } + + function _proofCheck( + address from, + address to, + uint256 tokenId, + TransferValidityProof[] calldata proofs + ) + internal + returns (bytes[] memory sealedKeys, IntelligentData[] memory newDatas) + { + AgentNFTStorage storage $ = _getAgentStorage(); + require(to != address(0), "Zero address"); + require($.tokens[tokenId].owner == from, "Not owner"); + require(proofs.length > 0, "Empty proofs array"); + + TransferValidityProofOutput[] memory proofOutput = $ + .verifier + .verifyTransferValidity(proofs); + + require( + proofOutput.length == $.tokens[tokenId].iDatas.length, + "Proof count mismatch" + ); + + sealedKeys = new bytes[](proofOutput.length); + newDatas = new IntelligentData[](proofOutput.length); + + for (uint i = 0; i < proofOutput.length; i++) { + // require the initial data hash is the same as the old data hash + require( + proofOutput[i].oldDataHash == + $.tokens[tokenId].iDatas[i].dataHash, + "Old data hash mismatch" + ); + + // only the receiver itself or the access assistant can sign the access proof + require( + proofOutput[i].accessAssistant == $.accessAssistants[to] || + proofOutput[i].accessAssistant == to, + "Access assistant mismatch" + ); + + bytes memory wantedKey = proofOutput[i].wantedKey; + bytes memory encryptedPubKey = proofOutput[i].encryptedPubKey; + if (wantedKey.length == 0) { + // if the wanted key is empty, the default wanted receiver is receiver itself + address defaultWantedReceiver = Utils.pubKeyToAddress( + encryptedPubKey + ); + require( + defaultWantedReceiver == to, + "Default wanted receiver mismatch" + ); + } else { + // if the wanted key is not empty, the data is private + require( + Utils.bytesEqual(encryptedPubKey, wantedKey), + "encryptedPubKey mismatch" + ); + } + + sealedKeys[i] = proofOutput[i].sealedKey; + newDatas[i] = IntelligentData({ + dataDescription: $.tokens[tokenId].iDatas[i].dataDescription, + dataHash: proofOutput[i].newDataHash + }); + } + return (sealedKeys, newDatas); + } + + function _transfer( + address from, + address to, + uint256 tokenId, + TransferValidityProof[] calldata proofs + ) internal { + AgentNFTStorage storage $ = _getAgentStorage(); + ( + bytes[] memory sealedKeys, + IntelligentData[] memory newDatas + ) = _proofCheck(from, to, tokenId, proofs); + + TokenData storage token = $.tokens[tokenId]; + token.owner = to; + token.approvedUser = address(0); + + delete token.iDatas; + for (uint i = 0; i < newDatas.length; i++) { + token.iDatas.push(newDatas[i]); + } + + emit Transferred(tokenId, from, to); + emit PublishedSealedKey(to, tokenId, sealedKeys); + } + + function iTransfer( + address to, + uint256 tokenId, + TransferValidityProof[] calldata proofs + ) public virtual { + require(_isApprovedOrOwner(msg.sender, tokenId), "Not authorized"); + _transfer(ownerOf(tokenId), to, tokenId, proofs); + } + + function transferFrom( + address from, + address to, + uint256 tokenId + ) public virtual { + TokenData storage token = _getAgentStorage().tokens[tokenId]; + require(_isApprovedOrOwner(msg.sender, tokenId), "Not authorized"); + require(to != address(0), "Zero address"); + require(token.owner == from, "Not owner"); + token.owner = to; + token.approvedUser = address(0); + + emit Transferred(tokenId, from, to); + } + + function iTransferFrom( + address from, + address to, + uint256 tokenId, + TransferValidityProof[] calldata proofs + ) public virtual { + require(_isApprovedOrOwner(msg.sender, tokenId), "Not authorized"); + _transfer(from, to, tokenId, proofs); + } + + function _clone( + address from, + address to, + uint256 tokenId, + TransferValidityProof[] calldata proofs + ) internal returns (uint256) { + AgentNFTStorage storage $ = _getAgentStorage(); + + ( + bytes[] memory sealedKeys, + IntelligentData[] memory newDatas + ) = _proofCheck(from, to, tokenId, proofs); + + uint256 newTokenId = $.nextTokenId++; + TokenData storage newToken = $.tokens[newTokenId]; + newToken.owner = to; + newToken.approvedUser = address(0); + + for (uint i = 0; i < newDatas.length; i++) { + newToken.iDatas.push(newDatas[i]); + } + + emit Cloned(tokenId, newTokenId, from, to); + emit PublishedSealedKey(to, newTokenId, sealedKeys); + + return newTokenId; + } + + function iClone( + address to, + uint256 tokenId, + TransferValidityProof[] calldata proofs + ) public virtual returns (uint256) { + require(_isApprovedOrOwner(msg.sender, tokenId), "Not authorized"); + return _clone(ownerOf(tokenId), to, tokenId, proofs); + } + + function iCloneFrom( + address from, + address to, + uint256 tokenId, + TransferValidityProof[] calldata proofs + ) public virtual returns (uint256) { + require(_isApprovedOrOwner(msg.sender, tokenId), "Not authorized"); + return _clone(from, to, tokenId, proofs); + } + + function authorizeUsage(uint256 tokenId, address to) public virtual { + require(to != address(0), "Zero address"); + AgentNFTStorage storage $ = _getAgentStorage(); + require($.tokens[tokenId].owner == msg.sender, "Not owner"); + + address[] storage authorizedUsers = $.tokens[tokenId].authorizedUsers; + for (uint i = 0; i < authorizedUsers.length; i++) { + require(authorizedUsers[i] != to, "Already authorized"); + } + + authorizedUsers.push(to); + emit Authorization(msg.sender, to, tokenId); + } + + function ownerOf(uint256 tokenId) public view virtual returns (address) { + AgentNFTStorage storage $ = _getAgentStorage(); + address owner = $.tokens[tokenId].owner; + require(owner != address(0), "Token does not exist"); + return owner; + } + + function authorizedUsersOf( + uint256 tokenId + ) public view virtual returns (address[] memory) { + AgentNFTStorage storage $ = _getAgentStorage(); + require(_exists(tokenId), "Token does not exist"); + return $.tokens[tokenId].authorizedUsers; + } + + function storageInfo( + uint256 tokenId + ) public view virtual returns (string memory) { + require(_exists(tokenId), "Token does not exist"); + return _getAgentStorage().storageInfo; + } + + function _exists(uint256 tokenId) internal view returns (bool) { + return _getAgentStorage().tokens[tokenId].owner != address(0); + } + + function intelligentDatasOf( + uint256 tokenId + ) public view virtual returns (IntelligentData[] memory) { + AgentNFTStorage storage $ = _getAgentStorage(); + require(_exists(tokenId), "Token does not exist"); + return $.tokens[tokenId].iDatas; + } + + function approve(address to, uint256 tokenId) public virtual { + address owner = ownerOf(tokenId); + require(to != owner, "Approval to current owner"); + require( + msg.sender == owner || isApprovedForAll(owner, msg.sender), + "Not authorized" + ); + + _getAgentStorage().tokens[tokenId].approvedUser = to; + emit Approval(owner, to, tokenId); + } + + function setApprovalForAll(address operator, bool approved) public virtual { + require(operator != msg.sender, "Approve to caller"); + _getAgentStorage().operatorApprovals[msg.sender][operator] = approved; + emit ApprovalForAll(msg.sender, operator, approved); + } + + function getApproved( + uint256 tokenId + ) public view virtual returns (address) { + require(_exists(tokenId), "Token does not exist"); + return _getAgentStorage().tokens[tokenId].approvedUser; + } + + function isApprovedForAll( + address owner, + address operator + ) public view virtual returns (bool) { + return _getAgentStorage().operatorApprovals[owner][operator]; + } + + function delegateAccess(address assistant) public virtual { + require(assistant != address(0), "Zero address"); + _getAgentStorage().accessAssistants[msg.sender] = assistant; + emit DelegateAccess(msg.sender, assistant); + } + + function getDelegateAccess( + address user + ) public view virtual returns (address) { + return _getAgentStorage().accessAssistants[user]; + } + + function _isApprovedOrOwner( + address spender, + uint256 tokenId + ) internal view returns (bool) { + require(_exists(tokenId), "Token does not exist"); + address owner = ownerOf(tokenId); + return (spender == owner || + getApproved(tokenId) == spender || + isApprovedForAll(owner, spender)); + } + + function batchAuthorizeUsage( + uint256 tokenId, + address[] calldata users + ) public virtual { + require(users.length > 0, "Empty users array"); + AgentNFTStorage storage $ = _getAgentStorage(); + require($.tokens[tokenId].owner == msg.sender, "Not owner"); + + for (uint i = 0; i < users.length; i++) { + require(users[i] != address(0), "Zero address in users"); + $.tokens[tokenId].authorizedUsers.push(users[i]); + emit Authorization(msg.sender, users[i], tokenId); + } + } + + function revokeAuthorization(uint256 tokenId, address user) public virtual { + AgentNFTStorage storage $ = _getAgentStorage(); + require($.tokens[tokenId].owner == msg.sender, "Not owner"); + require(user != address(0), "Zero address"); + + address[] storage authorizedUsers = $.tokens[tokenId].authorizedUsers; + bool found = false; + + for (uint i = 0; i < authorizedUsers.length; i++) { + if (authorizedUsers[i] == user) { + authorizedUsers[i] = authorizedUsers[ + authorizedUsers.length - 1 + ]; + authorizedUsers.pop(); + found = true; + break; + } + } + + require(found, "User not authorized"); + emit AuthorizationRevoked(msg.sender, user, tokenId); + } +} +``` + ## Security Considerations 1. **Proof Verification** From d472294e6ffa844b667c8e6d1f1c5c612bb740a1 Mon Sep 17 00:00:00 2001 From: wei Date: Wed, 10 Sep 2025 10:30:03 +0800 Subject: [PATCH 31/34] 1. put "verifyOwnership()" in backticks 2. expand "FHE" when first mention --- ERCS/erc-7857.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 59ef3d3aa7d..945c76613d8 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -64,7 +64,7 @@ The system supports two types of proofs: 1. **Ownership Proof** - Generated by Prover with access to original data - Proves knowledge of pre-images for claimed dataHashes - - Verified on-chain through verifyOwnership() + - Verified on-chain through `verifyOwnership()` 2. **Transfer Validity Proof** - Generated by Prover for data transfers @@ -360,7 +360,7 @@ The design choices in this standard are motivated by several key requirements: - Data cloning, where the data is cloned to a new token but the ownership is not transferred - Data usage authorization, where the data is authorized to be used by a specific user, but the ownership is not transferred, and the user still cannot access the data. This need an environment to authenticate the user and process the request from the authorized user secretly, we call it "Sealed Executor" -4. **Sealed Executor**: Although the Sealed Executor is not defined and out of the scope of this standard, it is a crucial component for the standard to work. The Sealed Executor is an environment that can authenticate the user and process the request from the authorized user secretly. The Sealed Executor should get authorized group by tokenId, and the verify the signature of the user using the public keys in the authorized group. If the verification is successful, the executor will process the request and return the result to the user, and the sealed executor could be implemented by a trusted party (where permitted), TEE, or FHE +4. **Sealed Executor**: Although the Sealed Executor is not defined and out of the scope of this standard, it is a crucial component for the standard to work. The Sealed Executor is an environment that can authenticate the user and process the request from the authorized user secretly. The Sealed Executor should get authorized group by tokenId, and the verify the signature of the user using the public keys in the authorized group. If the verification is successful, the executor will process the request and return the result to the user, and the sealed executor could be implemented by a trusted party (where permitted), TEE or Fully Homomorphic Encryption (FHE) ## Backwards Compatibility @@ -1113,7 +1113,7 @@ contract AgentNFT is 4. **Sealed Executor** - Although out of scope for this standard, the Sealed Executor is crucial for secure operation - The Sealed Executor authenticates users and processes requests in a secure environment by verifying user signatures against authorized public keys for each tokenId - - The Sealed Executor can be implemented through a trusted party (where permitted), Trusted Execution Environment (TEE), or Fully Homomorphic Encryption (FHE) + - The Sealed Executor can be implemented through a trusted party (where permitted), TEE or FHE - Ensuring secure request processing and result delivery ## Copyright From 3de8f848afb265720e52d8f19af7475c0ee1c5b7 Mon Sep 17 00:00:00 2001 From: wei Date: Wed, 10 Sep 2025 16:25:35 +0800 Subject: [PATCH 32/34] rename `intelligentDatasOf` as `intelligentDataOf` --- ERCS/erc-7857.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index 945c76613d8..d5668b17049 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -180,7 +180,7 @@ interface IERC7857Metadata { /// @notice Get the data hash of a token /// @param _tokenId The token identifier /// @return The current data hash of the token - function intelligentDatasOf(uint256 _tokenId) external view returns (IntelligentData[] memory); + function intelligentDataOf(uint256 _tokenId) external view returns (IntelligentData[] memory); } ``` @@ -993,7 +993,7 @@ contract AgentNFT is return _getAgentStorage().tokens[tokenId].owner != address(0); } - function intelligentDatasOf( + function intelligentDataOf( uint256 tokenId ) public view virtual returns (IntelligentData[] memory) { AgentNFTStorage storage $ = _getAgentStorage(); From fcf1c4de1395cdabff5b5a581702c93393d07488 Mon Sep 17 00:00:00 2001 From: wei Date: Tue, 23 Sep 2025 15:27:29 +0800 Subject: [PATCH 33/34] Update ERC-7857: Move to Last Call --- ERCS/erc-7857.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index d5668b17049..e683b10aaea 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -4,7 +4,8 @@ title: AI Agents NFT with Private Metadata description: AI agent NFTs, enabling secure management of private metadata with verifiable data transfer author: Ming Wu (@sparkmiw), Jason Zeng (@zenghbo), Wei Wu (@Wilbert957), Michael Heinrich (@michaelomg) discussions-to: https://ethereum-magicians.org/t/erc-7857-an-nft-standard-for-ai-agents-with-private-metadata/22391 -status: Review +status: Last Call +last-call-deadline: 2025-10-23 type: Standards Track category: ERC created: 2025-01-02 From 90c055ecf27d234f2e191ac4ca6b2135983e4f60 Mon Sep 17 00:00:00 2001 From: wei Date: Tue, 21 Oct 2025 14:32:16 +0800 Subject: [PATCH 34/34] Update ERC-7857: Move to Final --- ERCS/erc-7857.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index e683b10aaea..cd8647a4561 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -4,8 +4,7 @@ title: AI Agents NFT with Private Metadata description: AI agent NFTs, enabling secure management of private metadata with verifiable data transfer author: Ming Wu (@sparkmiw), Jason Zeng (@zenghbo), Wei Wu (@Wilbert957), Michael Heinrich (@michaelomg) discussions-to: https://ethereum-magicians.org/t/erc-7857-an-nft-standard-for-ai-agents-with-private-metadata/22391 -status: Last Call -last-call-deadline: 2025-10-23 +status: Final type: Standards Track category: ERC created: 2025-01-02 @@ -1121,4 +1120,4 @@ contract AgentNFT is Copyright and related rights waived via [CC0](../LICENSE.md). -[erc721]: ./eip-721.md \ No newline at end of file +[erc721]: ./eip-721.md