Proof & threat model

Every way a seller could try to keep the money.

This is the threat model the contracts are built against. Every row is covered by a unit test in the registry contract, and the attacks that can be sent as transactions were sent on testnet and refused.

  • 01Seller keeps withdrawing after the sale

    withdraw() checks the current NFT owner at call time

    ERR_NOT_OWNER
  • 02Seller sells while a withdrawal is in flight

    The token is locked for the withdrawal; nft_transfer rejects while locked

    ERR_TOKEN_LOCKED
  • 03Seller lists, then drains the vault before the buyer pays

    Withdrawals are blocked while the token has any approval

    ERR_TOKEN_LISTED
  • 04Seller keeps an access key on the vault

    Vaults are created with zero access keys and no upgrade method

    no AddKey
  • 05Anyone calls the vault directly

    The vault accepts calls only from the registry contract

    ERR_ONLY_REGISTRY
  • 06A stranger approves a derivative into canon

    approve() requires the parent's current owner

    ERR_NOT_PARENT_OWNER
  • 07A studio licenses work with a non-commercial ancestor

    buy_license() re-checks clearance across the whole lineage

    ERR_NOT_CLEARED
  • 08Splits over or under 100%

    Registration rejects splits that don't total 10,000 bps

    ERR_BAD_SPLITS
  • 09Rounding leaks or creates money

    Integer math; the remainder goes to the node's vault; sum equals price

    ERR_INVARIANT_SUM
  • 10An approved file is silently swapped

    The file hash is written once at registration; no method can change it

    no setter

Registry unit tests

41 passing

Attacks refused on testnet

11 of 11

Keys on the registry and vaults

0

Recorded on NEAR testnet

The whole story, as transactions.

Registry canonhq.testnet. Every row links to the block explorer. Shielded rows are attacks the contract refused.

  1. 01Publish vault code as NEP-591 global contract Anq6HuuCn243gdRdBjudqvR1uCe8NB8JQzTD5DF9KKtwArCB71ne… ↗
  2. 02Deploy registry to canonhq.testnet and initialise with the vault hashCXtZrr1A… ↗
  3. 03ada registers Ember FieldBZjdTYgQ… ↗
  4. 04ben registers Ember Field — Relief as a derivative of ember9dCPz73M… ↗
  5. 05ada approves Relief into canon6SiCx6AT… ↗
  6. 06dee registers Ember Field — Motion as a derivative of ember-relief7ECKs8sP… ↗
  7. 07ada (not the parent's owner) tries to approve Motionrefused · ERR_NOT_PARENT_OWNERHbPJrGFZ… ↗
  8. 08studio tries to license Motion before it is approvedrefused · ERR_NOT_CLEARED6Hrquxkq… ↗
  9. 09ben approves Motion into canon4iLLvMbh… ↗
  10. 10gil registers Ember Field — Nocturne as a derivative of ember4eiYEmno… ↗
  11. 11ada approves Nocturne4S2wbwSJ… ↗
  12. 12hal registers Ember Field — Pixel as a derivative of ember4frNdqus… ↗
  13. 13ivy registers Moth LightCooAeYpw… ↗
  14. 14jun registers Moth Light — Fragments as a derivative of moth-light3aPqBWSu… ↗
  15. 15ivy approves FragmentsHmtcuUaA… ↗
  16. 16studio tries to license Fragments (parent forbids commercial use)refused · ERR_NOT_CLEARED4bSvXfN9… ↗
  17. 17lea registers TidalDjRXAp5J… ↗
  18. 18nox registers Tidal — Neon Remix as a derivative of tidal7hmtbWTD… ↗
  19. 19lea approves Neon Remix2MTBMttt… ↗
  20. 20lea revokes Neon Remix8JKH48xc… ↗
  21. 21studio tries to license the revoked Neon Remixrefused · ERR_NOT_CLEARED4goc1MyP… ↗
  22. 22kai registers Quarry9cySegDz… ↗
  23. 23hal tries to derive from Quarry (derivatives closed)refused · ERR_DERIVATIVES_CLOSED6S7b5nYG… ↗
  24. 24ada registers splits totalling 90%refused · ERR_BAD_SPLITSGQNgq9Vr… ↗
  25. 25studio licenses Ember Field — Motion for 1.5 Ⓝ (splits: dee vault 0.9, fay 0.225, ben vault 0.21, cy 0.09, ada vault 0.075)HrgXySX1… ↗
  26. 26studio licenses Quarry for 0.8 ⓃEu9RgUUb… ↗
  27. 27eve (not the owner) tries to withdraw Relief's vaultrefused · ERR_NOT_OWNERAAeV3SwD… ↗
  28. 28ben lists Relief on a marketplace (nft_approve)6wr4yTjW… ↗
  29. 29ben tries to drain Relief's vault while it is listedrefused · ERR_TOKEN_LISTED6825sV2P… ↗
  30. 30eve's marketplace sells Relief for 1 Ⓝ via NEP-199 nft_transfer_payout: payout = ben 0.8, ember vault 0.2 (resale royalty)F2G9dcGT… ↗
  31. 31marketplace pays the seller ben 0.8 ⓃEwpCiJPV… ↗
  32. 32marketplace pays the 0.2 Ⓝ royalty straight into Ember's vaultEbYDjVU4… ↗
  33. 33ben (the seller) tries to withdraw after the salerefused · ERR_NOT_OWNERBV24rL8m… ↗
  34. 34eve calls Relief's vault release directly, bypassing the registryrefused · ERR_ONLY_REGISTRY6fxRFGxn… ↗
  35. 35eve (the buyer) withdraws 0.21 Ⓝ earned before she bought it663gSSN1… ↗
  36. 36cy claims 0.09 Ⓝ: her credit did not move with the salejDdxY5UA… ↗
  37. 37dee withdraws Motion's vault (0.9 Ⓝ)5X5uSG6h… ↗
  38. 38ada withdraws Ember's vault: 0.075 Ⓝ licence share + 0.2 Ⓝ resale royaltyBKJvR3Rr… ↗
  39. 39fay claims her 0.225 Ⓝ score shareArW18Fes… ↗
  40. 40fay tries to claim twicerefused · ERR_NOTHING_TO_CLAIM5mNeHjdt… ↗
  41. 41Delete registry access key ed25519:4T7JEo26gy…Ck5zh9yc… ↗