Skip to main content

ERC-7857: Technical Standard

Overviewโ€‹

ERC-7857 extends ERC-721 to support encrypted metadata, specifically designed for tokenizing AI agents and sensitive digital assets.

Prerequisites
  • Understanding of ERC-721 NFT standard
  • Basic cryptography knowledge (encryption, hashing)
  • Smart contract development experience
  • Familiarity with oracle systems

Document Purposeโ€‹

This page provides the technical specification, implementation details, and security considerations for ERC-7857. For high-level concepts, see the INFT Overview.

Key Technical Featuresโ€‹

FeatureDescriptionBenefit
Encrypted MetadataStore sensitive data securelyProtects proprietary AI models
Secure Re-encryptionTransfer without data exposureMaintains privacy during ownership changes
Oracle VerificationTEE/ZKP proof validationEnsures transfer integrity
Authorized UsageGrant access without ownershipEnables AI-as-a-Service models

Technical Specificationโ€‹

Core Interfaceโ€‹

interface IERC7857 is IERC721 {
// Transfer with metadata re-encryption
function transfer(
address from,
address to,
uint256 tokenId,
bytes calldata sealedKey,
bytes calldata proof
) external;

// Clone token with same metadata
function clone(
address to,
uint256 tokenId,
bytes calldata sealedKey,
bytes calldata proof
) external returns (uint256 newTokenId);

// Authorize usage without revealing data
function authorizeUsage(
uint256 tokenId,
address executor,
bytes calldata permissions
) external;
}

Transfer Architectureโ€‹

ERC-7857 Transfer Flow Diagram

Security Guarantees:

โœ… Metadata remains encrypted throughout process
โœ… Only new owner can decrypt transferred data
โœ… Transfer integrity cryptographically verified
โœ… No intermediary can access sensitive information

Oracle Implementationsโ€‹

ERC-7857 supports two oracle types for secure metadata re-encryption:

TEE (Trusted Execution Environment)โ€‹

How it works:

  1. Sender transmits encrypted data + key to TEE
  2. TEE securely decrypts data in isolated environment
  3. TEE generates new key and re-encrypts metadata
  4. TEE encrypts new key with receiver's public key
  5. TEE outputs sealed key and hash values

Advantages:

  • Hardware-level security guarantees
  • TEE can generate cryptographically secure keys
  • Attestation provides proof of secure execution
TEE Flow

TEE Implementation Exampleโ€‹

class TEEOracle {
async processTransfer(encryptedData, oldKey, receiverPublicKey) {
// All operations happen inside secure enclave
try {
// Step 1: Decrypt original data
const data = await this.decryptSecurely(encryptedData, oldKey);

// Step 2: Generate new encryption key
const newKey = await this.generateSecureKey();

// Step 3: Re-encrypt with new key
const newEncryptedData = await this.encryptSecurely(data, newKey);

// Step 4: Seal key for receiver
const sealedKey = await this.sealForReceiver(newKey, receiverPublicKey);

// Step 5: Generate attestation proof
const proof = await this.generateAttestation({
originalHash: hash(encryptedData),
newHash: hash(newEncryptedData),
receiverKey: receiverPublicKey
});

return {
newEncryptedData,
sealedKey,
proof
};
} catch (error) {
throw new Error(`TEE processing failed: ${error.message}`);
}
}
}

ZKP (Zero-Knowledge Proof)โ€‹

How it works:

  1. Sender provides old and new keys to ZKP system
  2. ZKP circuit verifies correct re-encryption
  3. Proof generated without revealing keys or data
  4. Smart contract validates ZKP proof

Considerations:

  • Cannot independently generate new keys
  • Requires sender to handle key generation
  • Receivers should rotate keys post-transfer
  • Computationally intensive proof generation
ZKP Flow

ZKP Circuit Exampleโ€‹

// ZKP circuit for verifying re-encryption
use ark_relations::r1cs::SynthesisError;

pub struct ReencryptionCircuit {
// Public inputs (known to verifier)
pub old_data_hash: Option<Fr>,
pub new_data_hash: Option<Fr>,
pub receiver_pubkey: Option<Fr>,

// Private inputs (known only to prover)
pub encrypted_data: Option<Vec<u8>>,
pub old_key: Option<Vec<u8>>,
pub new_key: Option<Vec<u8>>,
pub plaintext_data: Option<Vec<u8>>,
}

impl ConstraintSynthesizer<Fr> for ReencryptionCircuit {
fn generate_constraints(
self,
cs: ConstraintSystemRef<Fr>,
) -> Result<(), SynthesisError> {
// Step 1: Verify decryption of original data
let decrypted = decrypt_constraint(
cs.clone(),
&self.encrypted_data?,
&self.old_key?
)?;

// Step 2: Verify plaintext matches decrypted data
enforce_equal(
cs.clone(),
&decrypted,
&self.plaintext_data?
)?;

// Step 3: Verify re-encryption with new key
let reencrypted = encrypt_constraint(
cs.clone(),
&self.plaintext_data?,
&self.new_key?
)?;

// Step 4: Verify hash consistency
let computed_hash = hash_constraint(cs.clone(), &reencrypted)?;
enforce_equal(
cs,
&computed_hash,
&self.new_data_hash?
)?;

Ok(())
}
}

Implementation Guidelinesโ€‹

Smart Contract Architectureโ€‹

pragma solidity ^0.8.19;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract ERC7857 is ERC721, Ownable, ReentrancyGuard {
// State variables
mapping(uint256 => bytes32) private _metadataHashes;
mapping(uint256 => string) private _encryptedURIs;
mapping(uint256 => mapping(address => bytes)) private _authorizations;

// Oracle configuration
address public oracle;
uint256 public constant PROOF_VALIDITY_PERIOD = 1 hours;

// Events
event MetadataUpdated(uint256 indexed tokenId, bytes32 newHash);
event UsageAuthorized(uint256 indexed tokenId, address indexed executor);
event OracleUpdated(address oldOracle, address newOracle);

modifier validProof(bytes calldata proof) {
require(oracle != address(0), "Oracle not set");
require(IOracle(oracle).verifyProof(proof), "Invalid proof");
_;
}

function transfer(
address from,
address to,
uint256 tokenId,
bytes calldata sealedKey,
bytes calldata proof
) external nonReentrant validProof(proof) {
require(ownerOf(tokenId) == from, "Not owner");
require(to != address(0), "Invalid recipient");

// Update metadata access for new owner
_updateMetadataAccess(tokenId, to, sealedKey, proof);

// Transfer NFT ownership
_transfer(from, to, tokenId);

emit MetadataUpdated(tokenId, keccak256(sealedKey));
}

function _updateMetadataAccess(
uint256 tokenId,
address newOwner,
bytes calldata sealedKey,
bytes calldata proof
) internal {
// Verify proof contains correct metadata hash
bytes32 expectedHash = _extractHashFromProof(proof);
_metadataHashes[tokenId] = expectedHash;

// Store new encrypted URI if provided
string memory newURI = _extractURIFromProof(proof);
if (bytes(newURI).length > 0) {
_encryptedURIs[tokenId] = newURI;
}
}
}

Metadata Managementโ€‹

class MetadataManager {
constructor(storageProvider, encryptionService, options = {}) {
this.storage = storageProvider;
this.encryption = encryptionService;
this.options = {
keySize: 256,
algorithm: 'AES-GCM',
...options
};
}

async storeMetadata(data, ownerPublicKey) {
try {
// Validate input data
this._validateMetadata(data);

// Generate encryption key
const key = await this.encryption.generateKey({
size: this.options.keySize,
algorithm: this.options.algorithm
});

// Encrypt metadata
const encrypted = await this.encryption.encrypt(data, key, {
includeMac: true,
version: '1.0'
});

// Store encrypted data on distributed storage
const uri = await this.storage.store(encrypted, {
redundancy: 3,
availability: '99.9%'
});

// Seal key for owner using their public key
const sealedKey = await this.encryption.sealForOwner(
key,
ownerPublicKey
);

// Generate metadata hash for verification
const metadataHash = await this.encryption.hash(encrypted);

return {
uri,
sealedKey,
metadataHash,
algorithm: this.options.algorithm,
version: '1.0'
};
} catch (error) {
throw new Error(`Metadata storage failed: ${error.message}`);
}
}

async retrieveMetadata(uri, sealedKey, ownerPrivateKey) {
try {
// Fetch encrypted data from storage
const encrypted = await this.storage.retrieve(uri);

// Unseal the encryption key
const key = await this.encryption.unsealKey(
sealedKey,
ownerPrivateKey
);

// Decrypt and return metadata
const decrypted = await this.encryption.decrypt(encrypted, key);

return decrypted;
} catch (error) {
throw new Error(`Metadata retrieval failed: ${error.message}`);
}
}

_validateMetadata(data) {
if (!data || typeof data !== 'object') {
throw new Error('Invalid metadata format');
}

const maxSize = 10 * 1024 * 1024; // 10MB limit
const serialized = JSON.stringify(data);
if (serialized.length > maxSize) {
throw new Error('Metadata exceeds size limit');
}
}
}

Security Considerationsโ€‹

๐Ÿ”‘ Key Managementโ€‹

Best Practices:

  • Use hardware security modules (HSM) when available
  • Implement automatic key rotation every 90 days
  • Store private keys in secure enclaves or hardware wallets
  • Never log or expose private keys in error messages

Implementation:

class SecureKeyManager {
constructor(hsmProvider) {
this.hsm = hsmProvider;
this.keyRotationInterval = 90 * 24 * 60 * 60 * 1000; // 90 days
}

async generateKey() {
// Use HSM if available, fallback to secure random
return this.hsm ?
await this.hsm.generateKey() :
await crypto.subtle.generateKey(/*...*/);;
}
}

๐Ÿ”ฎ Oracle Securityโ€‹

TEE Verification:

  • Always verify TEE attestations before accepting proofs
  • Validate enclave signatures and measurement values
  • Implement attestation freshness checks

ZKP Auditing:

  • Audit circuit implementations thoroughly
  • Verify trusted setup parameters
  • Test edge cases and malformed inputs

Fallback Mechanisms:

contract OracleManager {
address[] public oracles;
uint256 public minConfirmations = 2;

function verifyWithFallback(bytes calldata proof) external view returns (bool) {
uint256 confirmations = 0;
for (uint i = 0; i < oracles.length; i++) {
if (IOracle(oracles[i]).verifyProof(proof)) {
confirmations++;
}
}
return confirmations >= minConfirmations;
}
}

๐Ÿ›ก๏ธ Metadata Privacyโ€‹

Encryption Standards:

  • Use AES-256-GCM for symmetric encryption
  • Implement RSA-4096 or ECC-P384 for key sealing
  • Always include authentication tags

Storage Security:

  • Encrypt metadata before network transmission
  • Use 0G Storage for decentralized, tamper-proof storage
  • Implement zero-knowledge access controls

Access Patterns:

// Secure metadata access pattern
async function accessMetadata(tokenId, requesterKey) {
// 1. Verify ownership or authorization
const isAuthorized = await verifyAccess(tokenId, requesterKey);
if (!isAuthorized) throw new Error('Unauthorized');

// 2. Retrieve encrypted metadata
const encrypted = await storage.retrieve(getMetadataURI(tokenId));

// 3. Decrypt only if authorized
const decrypted = await decrypt(encrypted, requesterKey);

return decrypted;
}

Advanced Featuresโ€‹

Clone Functionalityโ€‹

The clone() function allows creating copies of INFTs while maintaining metadata security:

function clone(
address to,
uint256 tokenId,
bytes calldata sealedKey,
bytes calldata proof
) external returns (uint256) {
require(canClone(tokenId, msg.sender), "Not authorized");

uint256 newTokenId = _mint(to);
_copyMetadata(tokenId, newTokenId, sealedKey, proof);

return newTokenId;
}

Authorized Usageโ€‹

Enable third parties to use INFT capabilities without ownership:

function authorizeUsage(
uint256 tokenId,
address executor,
bytes calldata permissions
) external {
require(ownerOf(tokenId) == msg.sender, "Not owner");

_authorizations[tokenId][executor] = permissions;

emit UsageAuthorized(tokenId, executor);
}

0G Infrastructure Integrationโ€‹

0G Storage Integrationโ€‹

// Store encrypted AI agent metadata
const metadata = {
model: aiAgent.serializedModel,
weights: aiAgent.trainedWeights,
config: aiAgent.configuration
};

const encrypted = await encryptMetadata(metadata, ownerPublicKey);
const storageResult = await ogStorage.store(encrypted, {
redundancy: 3,
durability: '99.999%'
});

console.log(`Metadata stored at: ${storageResult.uri}`);

0G Compute Integrationโ€‹

// Execute secure inference without exposing model
const inferenceResult = await ogCompute.executeSecure({
tokenId: inftId,
executor: authorizedExecutor,
input: userQuery,
verificationMode: 'TEE' // or 'ZKP'
});

// Result includes proof of correct execution
console.log(`Inference result: ${inferenceResult.output}`);
console.log(`Verification proof: ${inferenceResult.proof}`);

0G Chain Deploymentโ€‹

// Deploy INFT contract to 0G Chain
const ERC7857Factory = await ethers.getContractFactory('ERC7857');
const inftContract = await ERC7857Factory.deploy(
'AI Agent NFTs',
'AINFT',
oracleAddress,
ogStorageAddress
);

await inftContract.deployed();
console.log(`INFT contract deployed at: ${inftContract.address}`);

Resources & Referencesโ€‹

Official Documentationโ€‹

๐Ÿ“œ EIP-7857 Specification - Official Ethereum standard proposal
๐Ÿ’ป Reference Implementation - Complete codebase with examples
๐Ÿ”’ Security Audit Reports - Third-party security assessments (coming soon)

Community & Supportโ€‹

๐Ÿ’ฌ Developer Forum - Technical discussions and Q&A
๐Ÿ› GitHub Issues - Bug reports and feature requests
๐Ÿ“š Knowledge Base - Common implementation patterns

Standards & Specificationsโ€‹

๐Ÿ“„ ERC-721 Standard - Base NFT standard
๐Ÿ” Encryption Standards - NIST cryptography guidelines
๐Ÿ›ก๏ธ TEE Specifications - Intel SGX documentation

Next Stepsโ€‹

For Implementationโ€‹

๐Ÿš€ Integration Guide - Step-by-step development guide
๐ŸŽฏ Use Cases - Real-world implementation examples
๐Ÿ“‹ Best Practices Guide - Production deployment guidelines (coming soon)

For Testingโ€‹

๐Ÿงช Testnet Deployment - Test your implementation
๐Ÿ——๏ธ Oracle Testing - Verify TEE and ZKP implementations
๐Ÿ” Security Testing - Audit your contracts

Communityโ€‹

๐Ÿ’ฌ Join Discussions - Share implementations and get feedback
๐Ÿš€ Contribute - Help improve the standard and tooling
๐Ÿ“š Learn - Explore advanced features and optimizations