Skip to content

armsves/NoxShadowNFT

Repository files navigation

Nox Shadow NFT

Nox Shadow NFT is a privacy-first demo that combines:

  • Confidential NFTs (ownership + metadata pointer + file-key stored as Nox encrypted handles)
  • A factory for creating many collections
  • A cUSDC-only marketplace (Arbitrum Sepolia) using a “direct pay” flow compatible with Nox proofs
  • A modern Next.js UI (wallet connect + @iexec-nox/handle)

Privacy note: this makes state confidential (via handles). EVM transaction senders and cleartext calldata are still public.

Features

  • Confidential ownership: owner stored as encrypted uint256(uint160(address))
  • Private metadata pointer: stored as encrypted uint256 (you can resolve it to an IPFS metadata CID off-chain)
  • Encrypted file uploads: ciphertext stored on IPFS, AES key stored confidentially on-chain as an encrypted uint256
  • Selective disclosure: grant viewers handle access; “revocation” is rotation-based
  • Factory: create multiple collections from a standard implementation
  • Marketplace (Pattern B):
    • seller approves marketplace for NFT transfer execution
    • buyer pays seller directly in cUSDC via confidentialTransfer
    • buyer calls marketplace buyWithCtoken(...) to receive the NFT
    • optional “operator EOA” can preview by being granted viewer rights (off-chain decryption)

What this is (and isn’t)

  • Not ERC721: standard ERC721 logs leak ownership (Transfer(from,to,tokenId)), so this project avoids address-leaking transfer logs.
  • Most private possible within Nox on a public EVM:
    • encrypted on-chain state for ownership + metadata pointer + file key
    • public chain still reveals tx sender, timing, and any required clear arguments (e.g. recipient address for ACL)

Core idea

Nox currently supports encrypted runtime types including uint256. To represent an address:

  • Encode: ownerAsUint256 = uint256(uint160(ownerAddress))
  • Encrypt off-chain with @iexec-nox/handle:
    • encryptInput(ownerAsUint256, "uint256", registryAddress)
    • Send (handle, handleProof) to the contract
  • Decrypt (if you have ACL) and decode back to address:
    • address(uint160(decryptedUint256))

Privacy model

  • Ownership privacy: on-chain observers see only a bytes32 handle, not an address.
  • Selective disclosure: you can grant a third-party decryption rights for the current owner handle.
  • Revocation: NoxCompute does not provide removeViewer() for persistent ACL. So revocation is:
    • rotate the owner handle to a new handle
    • re-grant only the viewers you still want
    • any viewer who saved the old handle can still decrypt the old state (history can’t be erased)

Contracts (Solidity)

  • contracts/ConfidentialNftRegistry.sol: confidential NFT registry (ownership + metadata pointer + file key)
  • contracts/ConfidentialNftFactory.sol: creates new collections (EIP-1167 clones)
  • contracts/ConfidentialNftMarketplace.sol: cUSDC-only marketplace (direct pay + buy)

Deployed dependencies (Arbitrum Sepolia)

  • NoxCompute: 0xd464B198f06756a1d00be223634b85E0a731c229
  • cUSDC (accepted payment token): 0x1CCeC6bC60dB15E4055D43Dc2531BB7D4E5B808e

Frontend pages

  • /dashboard: cToken portfolio + Owned NFTs (detected by decrypting owner handles)
  • /confidential-nft: mint/transfer/metadata/file-key/ACL management
  • /marketplace: list + buy with cUSDC (direct pay flow)
  • /activity, /delegated-view: helper tools for cToken activity + viewer grants

Getting started

Prerequisites

  • Node.js 18+ (Hardhat v3 works best on newer Node)
  • A WalletConnect project ID (cloud.reown.com)

Install

npm install

Create .env.local (minimum):

NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=<your_project_id>
NEXT_PUBLIC_CONFIDENTIAL_NFT_ADDRESS=0x...
NEXT_PUBLIC_CONFIDENTIAL_NFT_MARKETPLACE_ADDRESS=0x...
# Optional (UI preview)
NEXT_PUBLIC_IPFS_GATEWAY=https://gateway.pinata.cloud/ipfs

Run the frontend

npm run dev

Deploy (Hardhat)

Set env vars:

export DEPLOYER_PRIVATE_KEY=0x...
export ARBITRUM_SEPOLIA_RPC_URL="https://arbitrum-sepolia.gateway.tenderly.co"
# For IPFS pinning (Next.js API routes):
export PINATA_JWT="..."
# Marketplace payment token (defaults in scripts can be overridden)
export MARKETPLACE_ACCEPTED_CTOKEN=0x1CCeC6bC60dB15E4055D43Dc2531BB7D4E5B808e

Compile + deploy (implementation + factory + marketplace + a first collection):

npm run compile:contracts
npm run deploy:confidential-nft

Then copy the printed addresses into .env.local as:

  • NEXT_PUBLIC_CONFIDENTIAL_NFT_ADDRESS
  • NEXT_PUBLIC_CONFIDENTIAL_NFT_MARKETPLACE_ADDRESS

E2E (Arbitrum Sepolia)

Run the end-to-end flow (mint → approve → list → pay cUSDC → buy → transfer):

npm run test:e2e:arbsepolia

Requires:

SELLER_PRIVATE_KEY=0x...
BUYER_PRIVATE_KEY=0x...
NEXT_PUBLIC_CONFIDENTIAL_NFT_ADDRESS=0x...
NEXT_PUBLIC_CONFIDENTIAL_NFT_MARKETPLACE_ADDRESS=0x...

Repo structure (relevant bits)

contracts/                       # ConfidentialNftRegistry.sol
scripts/                         # Hardhat deploy script
app/(app)/confidential-nft/      # Frontend test page
app/(app)/marketplace/           # Marketplace page
hooks/use-confidential-nft.ts    # Mint/transfer/rotate/grant/revoke
hooks/use-confidential-nft-disclosure.ts  # Grant list (Active/Outdated)
lib/confidential-nft-abi.ts      # ABI for viem/wagmi reads + events

Security / limitations

  • You cannot hide the tx sender on Arbitrum using Nox alone. For that, you need a relayer / ERC-4337 flow (separate layer).
  • You cannot delete what was already disclosed: a viewer can keep decrypted results for old handles.
  • Listings and marketplace actions still reveal some public metadata (seller, tokenId, listing price). The cUSDC transfer amount is confidential, but the listing price is public in this demo.
  • This is a demo-quality reference implementation; audit before production use.

License

MIT

About

Nft marketplace using nox encrypted NFT

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors