-
Notifications
You must be signed in to change notification settings - Fork 4
Description
Summary
Implement a fully-compliant ERC721 (NFT) contract with complete formal verification, demonstrating DumbContracts' capability for complex state models and ownership tracking.
Motivation
- Current examples: Simple state (single/few storage slots)
- ERC721 complexity: Token IDs, ownership tracking, approvals
- Market relevance: Second most important standard after ERC20
- Technical challenge: Tests finite set reasoning and complex mappings
- Proof of capability: Shows scalability to NFT platforms
Current State
All existing examples have simple state:
- SimpleStorage: Single value
- Counter: Single counter
- Ledger: Address → balance mapping
- SimpleToken: Similar to Ledger
ERC721 needs:
- Multiple interrelated mappings
- Token ID tracking
- Ownership transfers
- Approval system (token-level + operator-level)
Proposed Implementation
State Model
structure ERC721State where
-- Core mappings
owners : Nat → Address -- tokenId → owner
balances : Address → Nat -- owner → token count
tokenApprovals : Nat → Address -- tokenId → approved address
operatorApprovals : (Address × Address) → Bool -- (owner, operator) → approved
-- Tracking
knownTokenIds : FiniteNatSet -- All minted tokens
knownOwners : FiniteAddressSet -- All addresses with tokens
-- Metadata
totalSupply : Nat
nextTokenId : Nat -- For sequential mintingERC721 Interface
structure ERC721Spec where
-- Views
balanceOf : Address → ContractState → Nat
ownerOf : Nat → ContractState → Option Address
getApproved : Nat → ContractState → Option Address
isApprovedForAll : Address → Address → ContractState → Bool
-- Mutations
mint : Address → Spec -- Mint to address
burn : Nat → Spec -- Burn token
transferFrom : Address → Address → Nat → Spec
approve : Address → Nat → Spec -- Approve specific token
setApprovalForAll : Address → Bool → Spec -- Approve all tokens
-- Events
emitTransfer : Address → Address → Nat → Event
emitApproval : Address → Address → Nat → Event
emitApprovalForAll : Address → Address → Bool → EventProperties to Prove (30+)
Uniqueness Properties (5)
- Unique ownership: Each tokenId has exactly one owner
- No phantom tokens: tokenId exists ⇒ owner exists
- Balance consistency: balances[owner] = #{tokens owned by owner}
- Supply consistency: totalSupply = |knownTokenIds|
- Owner set consistency: knownOwners = {addr | balances[addr] > 0}
Transfer Properties (7)
- Transfer changes owner: transferFrom sets new owner
- Transfer updates balances: Decreases sender, increases recipient
- Transfer preserves supply: totalSupply unchanged
- Transfer requires ownership: Reverts if not owner or approved
- Transfer clears approval: Token approval reset after transfer
- Transfer to zero fails: Can't transfer to 0x0 address
- Transfer self-preserves: transfer(self, tokenId) preserves ownership
Approval Properties (8)
- Approve authorization: Only owner can approve
- Approve sets allowance: getApproved returns approved address
- Approve emits event: Approval event emitted
- Operator approval independence: setApprovalForAll doesn't affect token approvals
- Operator approval symmetric: isApprovedForAll is symmetric for (owner, operator)
- Approval clears on transfer: Token approval cleared when transferred
- Self-approval allowed: Can approve self (no-op)
- Zero address approval: approve(0x0, tokenId) clears approval
Mint Properties (5)
- Mint increases supply: totalSupply increases by 1
- Mint increases balance: balances[to] increases by 1
- Mint creates ownership: ownerOf(newTokenId) = to
- Mint no duplicate IDs: Can't mint same tokenId twice
- Mint emits Transfer: Transfer(0x0, to, tokenId) emitted
Burn Properties (5)
- Burn decreases supply: totalSupply decreases by 1
- Burn decreases balance: balances[owner] decreases by 1
- Burn removes ownership: ownerOf(tokenId) = None after burn
- Burn requires ownership: Only owner can burn
- Burn emits Transfer: Transfer(owner, 0x0, tokenId) emitted
Implementation Phases
Phase 1: State Model & Spec (1 week)
- Define ERC721State structure
- Implement FiniteNatSet for token tracking
- Define ERC721Spec
Phase 2: EDSL Implementation (1 week)
- Implement core functions (mint, burn, transfer)
- Implement approval functions
- Add event emission
Phase 3: Uniqueness Proofs (1 week)
- Prove properties 1-5
- Requires sum properties over finite sets
- Similar to Ledger sum proofs
Phase 4: Transfer & Approval Proofs (1 week)
- Prove properties 6-20
- Complex ownership tracking
- Approval state transitions
Phase 5: Mint & Burn Proofs (3 days)
- Prove properties 21-30
- Supply consistency
- Edge cases
Phase 6: Testing & Documentation (2 days)
- Property tests for all theorems
- Differential testing
- Coverage validation
Technical Challenges
Challenge 1: Finite Token Set Tracking
Problem: Need to prove balances[owner] = count of owned tokens
Solution: Use FiniteNatSet similar to FiniteAddressSet
def FiniteNatSet := FiniteSet Nat
def countOwnedTokens (owner : Address) (state : ERC721State) : Nat :=
state.knownTokenIds.elements.filter (fun tokenId =>
state.owners tokenId = owner
).length
theorem balance_equals_count (owner : Address) (state : ERC721State) :
state.balances owner = countOwnedTokens owner stateChallenge 2: Approval State Complexity
Problem: Two-level approval (token-level + operator-level)
Solution: Clear precedence rules and independence proofs
Challenge 3: Sum Properties
Problem: Need to prove Σ balances = totalSupply
Solution: Reuse patterns from Ledger (#65)
Dependencies
- Issue [Proofs] Complete Ledger sum property helper lemmas #65: Ledger sum proofs (patterns for finite set reasoning)
- Issue [Integration] Integrate EVMYulLean UInt256 arithmetic library #67: EVMYulLean UInt256 (for safe arithmetic)
- Issue [Example Contract] Implement ERC20 standard token with full verification #69: ERC20 implementation (similar approval patterns)
Impact
✅ ERC721 is second most important standard
✅ Complex state pushes verification limits
✅ Demonstrates NFT platform capability
✅ Attracts NFT project audits
✅ Template for ERC1155 (multi-token standard)
Estimated Effort
4 weeks for experienced Lean developer
Success Criteria
- Full ERC721 compliance (per EIP-721)
- All 30+ properties proven (no
sorry) - Property tests for all theorems
- CI passes
- Documentation complete
- Differential tests against reference ERC721
Related Issues
- Builds on ERC20 ([Example Contract] Implement ERC20 standard token with full verification #69)
- Requires sum proofs ([Proofs] Complete Ledger sum property helper lemmas #65)
- Uses verified arithmetic ([Integration] Integrate EVMYulLean UInt256 arithmetic library #67)
Future Work
- ERC721Enumerable extension
- ERC721Metadata extension (name, symbol, tokenURI)
- ERC1155 multi-token standard