{
 "slug": "erc-7730-vs-manav-statement-v1-descriptor-registries",
 "topic_id": "TOPIC-074",
 "cluster": "Digital Asset Custody",
 "tier": "Tier B",
 "title": "Descriptor registries solve blind signing by adding a dependency",
 "summary": "Clear-signing standards let a wallet render a contract call by looking up a descriptor. The descriptor comes from somewhere, and that somewhere becomes part of the trust boundary.",
 "lede": "If a wallet cannot understand a contract call, give it a file explaining how to display one. The approach works, and it makes the display correctness depend on a file the wallet did not previously need.",
 "date": "2024-08-17",
 "category": "Comparison",
 "author_id": "priya-venkatraman",
 "tags": [
  "ERC-7730",
  "clear signing",
  "descriptors",
  "supply chain",
  "wallet security",
  "standards"
 ],
 "image_title": "Descriptor Registries Attack Surface",
 "schema": "Article",
 "key_takeaways": [
  "Descriptor-based clear signing is a genuine improvement over displaying a hash, and it introduces a new dependency.",
  "A poisoned or substituted descriptor produces a plausible, wrong rendering — which is worse than an obvious warning.",
  "Pinning the schema from the executing system removes the external lookup and the substitution opportunity with it."
 ],
 "body": [
  {
   "type": "h2",
   "text": "The problem descriptors address"
  },
  {
   "type": "diagram",
   "kind": "flow",
   "alt": "Where the rendering comes from",
   "caption": "The user judges the rendering. The rendering comes from somewhere else.",
   "nodes": [
    {
     "label": "Contract call constructed",
     "note": "by the dapp"
    },
    {
     "label": "Descriptor looked up",
     "note": "registry or the dapp itself",
     "bad": true
    },
    {
     "label": "Wallet renders from descriptor",
     "note": "trusted output",
     "bad": true
    },
    {
     "label": "User approves what they read",
     "note": "not what executes",
     "bad": true
    }
   ]
  },
  {
   "type": "p",
   "html": "A wallet receiving a contract call sees a target address and a blob of calldata. Without knowing the contract's interface it cannot say what the call does, so it displays a hash and a warning."
  },
  {
   "type": "p",
   "html": "A descriptor supplies the missing knowledge: here is what this function is called, here are its parameters, here is how to display them for a human. The wallet renders something meaningful."
  },
  {
   "type": "p",
   "html": "This is a real improvement over a hash, and the standards work behind it is worth supporting. The question is where the descriptor comes from."
  },
  {
   "type": "h2",
   "text": "The new dependency"
  },
  {
   "type": "table",
   "head": [
    "Question",
    "Consequence"
   ],
   "rows": [
    [
     "Who publishes the descriptor?",
     "That party can influence what users see"
    ],
    [
     "How is it fetched?",
     "A network dependency in the signing path"
    ],
    [
     "Is it authenticated?",
     "If not, anything in the path can substitute it"
    ],
    [
     "What if it is missing?",
     "Fall back to blind signing — or fail closed?"
    ],
    [
     "Who can update it?",
     "An update changes the rendering for future signers"
    ]
   ]
  },
  {
   "type": "p",
   "html": "Each of these has a good answer available. The concern is that a partial deployment — descriptors fetched over the network, cached, unauthenticated, with a fallback — is the easy implementation and the weak one."
  },
  {
   "type": "h2",
   "text": "Why a wrong rendering is worse than a warning"
  },
  {
   "type": "p",
   "html": "This is the crux."
  },
  {
   "type": "code",
   "text": "# Without a descriptor\n  \"Unknown contract call. Blind signing.\n   Hash: 0x9c1f8a3e...\"\n  → the user knows they are not verifying anything\n\n# With a poisoned descriptor\n  \"Approve token transfer\n   Amount:    100 USDC\n   Recipient: Treasury Operations\"\n  → the user believes they verified it\n  → the call actually does something else entirely"
  },
  {
   "type": "p",
   "html": "An honest warning preserves the user's accurate model of their own uncertainty. A confident wrong rendering destroys it, and the user proceeds with more confidence than in the unmitigated case."
  },
  {
   "type": "h2",
   "text": "The substitution paths"
  },
  {
   "type": "p",
   "html": "Assuming descriptors are fetched rather than pinned, the opportunities are the ordinary supply chain ones."
  },
  {
   "type": "ul",
   "items": [
    "Compromise of the registry or its publishing process",
    "A malicious or compromised descriptor author for a specific contract",
    "Interception between the wallet and the registry, if authentication is weak",
    "Cache poisoning, where a wallet retains a substituted descriptor",
    "An update that changes rendering for a contract already in use"
   ]
  },
  {
   "type": "p",
   "html": "None of these is exotic. They are the same categories that affect package registries and content delivery, applied to a file that determines what a user believes they are signing."
  },
  {
   "type": "h2",
   "text": "The alternative: pin from the executing system"
  },
  {
   "type": "p",
   "html": "If the system that will execute the action supplies the schema, and its hash is included in what gets signed, the lookup disappears."
  },
  {
   "type": "code",
   "text": "statement = {\n  \"schema_hash\": \"sha256:2b7d...4056\",   # pinned, in the signed data\n  \"action\": \"treasury.transfer\",\n  \"asset\": \"USDC\",\n  \"amount\": \"100.00\",\n  \"to\": \"0x...\",\n  \"to_label\": \"Treasury Operations\",\n  \"chain_id\": 1\n}\n\n# The schema that governs the rendering is identified inside\n# the signature. A substituted schema produces a different\n# hash and the verification fails."
  },
  {
   "type": "p",
   "html": "The trade-off is honest: this works where the executing system is known and can supply the schema. It does not solve the open case of a wallet encountering an arbitrary contract from an unknown source, which is the case descriptor registries were designed for."
  },
  {
   "type": "h2",
   "text": "Comparing the two fairly"
  },
  {
   "type": "table",
   "head": [
    "",
    "Descriptor registry",
    "Pinned schema"
   ],
   "rows": [
    [
     "Covers arbitrary unknown contracts",
     "Yes",
     "No"
    ],
    [
     "External dependency at signing",
     "Yes",
     "No"
    ],
    [
     "Substitution risk",
     "Present, mitigable",
     "Eliminated by construction"
    ],
    [
     "Deployment effort",
     "Ecosystem-wide",
     "Per integrating system"
    ],
    [
     "Right for",
     "Consumer wallets meeting new contracts",
     "Institutional flows with known counterparties"
    ]
   ]
  },
  {
   "type": "p",
   "html": "These are complementary rather than competing. A consumer wallet needs the registry approach because it genuinely meets arbitrary contracts. An institutional custody flow interacting with a known set of contracts does not, and should not accept the dependency."
  },
  {
   "type": "h2",
   "text": "If you deploy descriptors"
  },
  {
   "type": "ol",
   "items": [
    "Authenticate descriptors with a signature from a party you have chosen to trust, not merely a TLS connection to a registry.",
    "Pin descriptor versions for contracts you use regularly, and treat updates as a change requiring review.",
    "Fail closed when a descriptor is missing. A fallback to blind signing is a fallback an attacker can induce.",
    "Display the descriptor's source and version, so a user can see what is rendering their transaction.",
    "Verify the descriptor matches the contract's actual interface, where the contract is verified and the interface is available."
   ]
  },
  {
   "type": "h2",
   "text": "A worked example: substitution paths"
  },
  {
   "type": "table",
   "caption": "Four ways the rendering can be wrong",
   "head": [
    "Path",
    "Effect"
   ],
   "rows": [
    [
     "Malicious descriptor submitted",
     "Wallet renders a benign description of a harmful call"
    ],
    [
     "Registry compromise",
     "Same, at scale"
    ],
    [
     "Wrong descriptor matched",
     "Honest error, identical outcome"
    ],
    [
     "Stale descriptor after upgrade",
     "Renders the old semantics for new code"
    ],
    [
     "<strong style=\"font-weight:600\">Pinned from the executing system</strong>",
     "<strong style=\"font-weight:600\">Rendering and execution come from the same place</strong>"
    ]
   ]
  },
  {
   "type": "p",
   "html": "A warning tells the user to be careful. A confident wrong rendering tells them not to be, which is why the failure mode here is worse than the problem it replaced."
  },
  {
   "type": "h2",
   "text": "Objections and honest limits"
  },
  {
   "type": "p",
   "html": "<strong style=\"font-weight:600\">“So descriptors are a bad idea.”</strong> No — they are a clear improvement over blind hex and should be deployed. The point is to be precise about what has been achieved: readability, not authenticity."
  },
  {
   "type": "p",
   "html": "<strong style=\"font-weight:600\">“Pinning from the executing system has its own trust assumption.”</strong> It does, and it is a narrower one. You are trusting the system that will perform the action to describe the action, rather than trusting a third party to describe someone else's code correctly."
  }
 ],
 "faq": [
  {
   "q": "Are descriptor registries a bad idea?",
   "a": "No. They solve a genuine problem for wallets meeting arbitrary contracts. The concern is the new dependency and how partial deployments handle it."
  },
  {
   "q": "Why is a wrong rendering worse than a warning?",
   "a": "A warning preserves the user's accurate sense of uncertainty. A confident wrong rendering makes them believe they verified something they did not."
  },
  {
   "q": "When is a pinned schema appropriate?",
   "a": "When the executing system is known and can supply it — institutional flows with defined counterparties. It does not cover arbitrary unknown contracts."
  },
  {
   "q": "What is the most important deployment rule?",
   "a": "Fail closed when a descriptor is missing. A fallback to blind signing is one an attacker can induce."
  },
  {
   "q": "Are descriptor registries worse than blind signing?",
   "a": "No. They are a clear improvement. The caution is that a confident wrong rendering is more dangerous than an obvious warning."
  },
  {
   "q": "What happens when a contract is upgraded?",
   "a": "The descriptor can go stale and render old semantics for new code, confidently and without warning."
  },
  {
   "q": "What does signing the rendering change?",
   "a": "The user's signature covers what they actually read, so a later dispute is about a fact rather than an inference."
  }
 ],
 "sources": [
  {
   "t": "ERC-7730 — Structured data clear signing format",
   "u": "https://eips.ethereum.org/EIPS/eip-7730"
  },
  {
   "t": "EIP-712 — Typed structured data hashing and signing",
   "u": "https://eips.ethereum.org/EIPS/eip-712"
  },
  {
   "t": "Published research on supply chain attacks against metadata registries."
  },
  {
   "t": "RFC 8785 — JSON Canonicalization Scheme",
   "u": "https://www.rfc-editor.org/rfc/rfc8785"
  }
 ],
 "related": [
  {
   "slug": "bybit-wazirx-multisig-hack-payload-mismatch-postmortem",
   "title": "The payload flip",
   "category": "Crypto"
  },
  {
   "slug": "hardware-wallet-blind-signing-ledger-trezor-screens-cant",
   "title": "Why hardware wallet screens cannot protect you",
   "category": "Crypto"
  },
  {
   "slug": "eip-712-structured-data-hashing-hidden-chainid-divergence",
   "title": "EIP-712 and chain id divergence",
   "category": "Crypto"
  }
 ],
 "image": "https://cdn.twc.sh/images/igcache/Descriptor%20Registries%20Attack%20Surface/1500_900/blog.jpg",
 "wordcount": 1063,
 "url": "/blog/erc-7730-vs-manav-statement-v1-descriptor-registries.html",
 "reading_time": "5 min read",
 "seo_title": "Descriptor registries as a supply-chain dependency",
 "meta_description": "Clear-signing standards let a wallet render a contract call by looking up a descriptor — which becomes part of the trust boundary.",
 "hub": {
  "slug": "topics/digital-asset-custody",
  "title": "Digital asset custody"
 },
 "answer": "Whoever controls the descriptor. Clear-signing lets a wallet render a contract call by looking up a description of it, which is a real improvement over hex. The description comes from a registry, and a wrong description is more dangerous than no description at all.",
 "answer_q": "What does a descriptor registry add to the trust boundary?",
 "glossary": [
  {
   "term": "Clear signing",
   "def": "Rendering a contract call in human-readable terms rather than presenting raw calldata."
  },
  {
   "term": "Descriptor",
   "def": "A machine-readable description of how to render a contract call, resolved from a registry."
  },
  {
   "term": "Pinning",
   "def": "Having the executing system supply the rendering, so description and execution share a source."
  }
 ],
 "checklist": {
  "title": "If you deploy descriptors",
  "id": "descriptors",
  "desc": "Five steps.",
  "steps": [
   {
    "name": "Know who can write to the registry.",
    "text": "And what review applies."
   },
   {
    "name": "Handle the missing-descriptor case explicitly.",
    "text": "Silence should not look like safety."
   },
   {
    "name": "Treat upgrades as descriptor-invalidating.",
    "text": "Stale semantics render confidently."
   },
   {
    "name": "Prefer descriptors pinned by the executing system.",
    "text": "Rendering and execution from one source."
   },
   {
    "name": "Sign what was rendered, not the payload.",
    "text": "So the dispute has an answer."
   }
  ]
 },
 "cta": {
  "title": "Where this fits in Manav",
  "html": "Manav signs the rendering itself — the exact bytes the person read — so approval and execution cannot silently differ.",
  "href": "../docs.html",
  "label": "See rendered-effect signing"
 }
}