Skip to content

Add clear signing display metadata to the IDL - #171

Open
lorisleiva wants to merge 1 commit into
mainfrom
loris/clear-signing-idl
Open

Add clear signing display metadata to the IDL#171
lorisleiva wants to merge 1 commit into
mainfrom
loris/clear-signing-idl

Conversation

@lorisleiva

Copy link
Copy Markdown
Member

This PR enriches the Codama IDL with display metadata for clear signing, per sRFC 39, and bumps codama to ^1.10.0 (the display node types shipped in 1.9). All 28 token instructions and the 3 associated-token-account instructions gain intents, interpolated intents and labels.

Token amounts scale through the provide/inject graph:

  • Checked variants provide decimals from their own decimals argument — fully offline, ideal for hardware wallets; the redundant argument is hidden once consumed (skip: "whenInjected").
  • mintTo/burn provide decimals from the mint account (accountFieldValueNode + accountLink), degrading gracefully to raw without account access.
  • Amounts with no mint in reach (transfer, approve) render explicitly as base units.

Sample renders via @codama/dynamic-instructions (the mint address resolves to a symbol when the renderer names it):

Transfer 1.5 EPjFW… from CiDwV… to DRpbC…
Mint 1.5 EPjFW… to HN7cA…
Transfer 1500000 base units from CiDwV… to DRpbC…
Close 4Nd1m… and send its balance to 9WzDX…

Clients were regenerated and are unaffected: renderers pass display metadata through untouched.

@lorisleiva

Copy link
Copy Markdown
Member Author

Rendered output for review

To make this JSON diff easier to review, every instruction below is rendered end-to-end through @codama/dynamic-instructions#getInstructionDisplay using synthetic sample data. Each collapsed line shows the interpolated intent at a glance (addresses abridged); expand for the full render showing both presentation modes a wallet chooses between — the interpolated sentence, or the intent-with-fields fallback screen. Address formatting (truncation, .sol names, token symbols) is left to the consuming wallet, so raw base58 appears in the full renders.

Reviewer note: the mint address in amount sentences renders as a symbol (e.g. 1.5 USDC) whenever the wallet's naming layer knows it. A few fields render as {"__option":"Some",…} or numeric enum indices — known presentation gaps in @codama/dynamic-instructions (option unwrapping, scalar-enum lookup) being fixed upstream, not IDL issues.

initializeMint — Initialize mint EPjFW…Dt1v with 6 decimals
Interpolated intent:
│ Initialize mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v with 6 decimals

Intent + fields (fallback):
│ Initialize mint
│   Decimals: 6
│   Mint Authority: 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj
│   Freeze Authority: {"__option":"Some","value":"7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj"}
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
initializeAccount — Initialize token account DRpbC…21hy for mint EPjFW…Dt1v
Interpolated intent:
│ Initialize token account DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy for mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

Intent + fields (fallback):
│ Initialize token account
│   Token Account: DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
│   Owner: 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj
initializeMultisig — Initialize multisig CiDwV…AQz3 requiring 2 signatures
Interpolated intent:
│ Initialize multisig CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3 requiring 2 signatures

Intent + fields (fallback):
│ Initialize multisig
│   Required Signatures: 2
│   Multisig: CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3
transfer — Transfer 1500000 base units from CiDwV…AQz3 to DRpbC…21hy
Interpolated intent:
│ Transfer 1500000 base units from CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3 to DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy

Intent + fields (fallback):
│ Transfer tokens
│   Amount: 1500000 base units
│   From: CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3
│   To: DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy
│   Authority: 3Wnd5Df69KitZfUoPYZU438eFRNwGHkhLnSAWL65PxJX
approve — Approve HN7cA…YWrH to spend 1500000 base units from CiDwV…AQz3
Interpolated intent:
│ Approve HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH to spend 1500000 base units from CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3

Intent + fields (fallback):
│ Approve delegate
│   Amount: 1500000 base units
│   Token Account: CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3
│   Delegate: HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH
│   Owner: 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj
revoke — Revoke the delegate of CiDwV…AQz3
Interpolated intent:
│ Revoke the delegate of CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3

Intent + fields (fallback):
│ Revoke delegate
│   Token Account: CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3
│   Owner: 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj
setAuthority — Change an authority of DRpbC…21hy
Interpolated intent:
│ Change an authority of DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy

Intent + fields (fallback):
│ Set authority
│   Authority Type: 1
│   New Authority: {"__option":"Some","value":"3Wnd5Df69KitZfUoPYZU438eFRNwGHkhLnSAWL65PxJX"}
│   Account: DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy
│   Owner: 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj
mintTo — Mint 1.5 EPjFW…Dt1v to HN7cA…YWrH
Interpolated intent:
│ Mint 1.5 EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v to HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH

Intent + fields (fallback):
│ Mint tokens
│   Amount: 1.5
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
│   To: HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH
│   Mint Authority: 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj
burn — Burn 1.5 EPjFW…Dt1v from DRpbC…21hy
Interpolated intent:
│ Burn 1.5 EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v from DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy

Intent + fields (fallback):
│ Burn tokens
│   Amount: 1.5
│   Token Account: DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
│   Authority: 3Wnd5Df69KitZfUoPYZU438eFRNwGHkhLnSAWL65PxJX
closeAccount — Close DRpbC…21hy and send its balance to DRpbC…21hy
Interpolated intent:
│ Close DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy and send its balance to DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy

Intent + fields (fallback):
│ Close token account
│   Token Account: DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy
│   Recipient: DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy
│   Owner: 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj
freezeAccount — Freeze DRpbC…21hy
Interpolated intent:
│ Freeze DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy

Intent + fields (fallback):
│ Freeze token account
│   Token Account: DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
│   Owner: 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj
thawAccount — Thaw DRpbC…21hy
Interpolated intent:
│ Thaw DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy

Intent + fields (fallback):
│ Thaw token account
│   Token Account: DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
│   Owner: 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj
transferChecked — Transfer 1.5 EPjFW…Dt1v from CiDwV…AQz3 to DRpbC…21hy
Interpolated intent:
│ Transfer 1.5 EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v from CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3 to DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy

Intent + fields (fallback):
│ Transfer tokens
│   Amount: 1.5
│   From: CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
│   To: DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy
│   Authority: 3Wnd5Df69KitZfUoPYZU438eFRNwGHkhLnSAWL65PxJX
approveChecked — Approve HN7cA…YWrH to spend 1.5 EPjFW…Dt1v from CiDwV…AQz3
Interpolated intent:
│ Approve HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH to spend 1.5 EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v from CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3

Intent + fields (fallback):
│ Approve delegate
│   Amount: 1.5
│   Token Account: CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
│   Delegate: HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH
│   Owner: 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj
mintToChecked — Mint 1.5 EPjFW…Dt1v to HN7cA…YWrH
Interpolated intent:
│ Mint 1.5 EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v to HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH

Intent + fields (fallback):
│ Mint tokens
│   Amount: 1.5
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
│   To: HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH
│   Mint Authority: 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj
burnChecked — Burn 1.5 EPjFW…Dt1v from DRpbC…21hy
Interpolated intent:
│ Burn 1.5 EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v from DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy

Intent + fields (fallback):
│ Burn tokens
│   Amount: 1.5
│   Token Account: DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
│   Authority: 3Wnd5Df69KitZfUoPYZU438eFRNwGHkhLnSAWL65PxJX
initializeAccount2 — Initialize token account DRpbC…21hy for mint EPjFW…Dt1v
Interpolated intent:
│ Initialize token account DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy for mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

Intent + fields (fallback):
│ Initialize token account
│   Owner: 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj
│   Token Account: DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
syncNative — Sync the wrapped SOL balance of DRpbC…21hy
Interpolated intent:
│ Sync the wrapped SOL balance of DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy

Intent + fields (fallback):
│ Sync wrapped SOL
│   Token Account: DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy
initializeAccount3 — Initialize token account DRpbC…21hy for mint EPjFW…Dt1v
Interpolated intent:
│ Initialize token account DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy for mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

Intent + fields (fallback):
│ Initialize token account
│   Owner: 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj
│   Token Account: DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
initializeMultisig2 — Initialize multisig CiDwV…AQz3 requiring 2 signatures
Interpolated intent:
│ Initialize multisig CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3 requiring 2 signatures

Intent + fields (fallback):
│ Initialize multisig
│   Required Signatures: 2
│   Multisig: CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3
initializeMint2 — Initialize mint EPjFW…Dt1v with 6 decimals
Interpolated intent:
│ Initialize mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v with 6 decimals

Intent + fields (fallback):
│ Initialize mint
│   Decimals: 6
│   Mint Authority: 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj
│   Freeze Authority: {"__option":"Some","value":"7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj"}
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
getAccountDataSize — Get the account size for mint EPjFW…Dt1v
Interpolated intent:
│ Get the account size for mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

Intent + fields (fallback):
│ Get account data size
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
initializeImmutableOwner — Make the owner of DRpbC…21hy immutable
Interpolated intent:
│ Make the owner of DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy immutable

Intent + fields (fallback):
│ Initialize immutable owner
│   Token Account: DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy
amountToUiAmount(fallback only) Convert amount to UI amount
Intent + fields:
│ Convert amount to UI amount
│   Amount: 1500000 base units
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
uiAmountToAmount(fallback only) Convert UI amount to amount
Intent + fields:
│ Convert UI amount to amount
│   UI Amount: 1.5
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
withdrawExcessLamports — Withdraw excess SOL from CiDwV…AQz3 to DRpbC…21hy
Interpolated intent:
│ Withdraw excess SOL from CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3 to DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy

Intent + fields (fallback):
│ Withdraw excess SOL
│   From: CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3
│   To: DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy
│   Authority: 3Wnd5Df69KitZfUoPYZU438eFRNwGHkhLnSAWL65PxJX
unwrapLamports — Unwrap SOL from CiDwV…AQz3 to DRpbC…21hy
Interpolated intent:
│ Unwrap SOL from CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3 to DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy

Intent + fields (fallback):
│ Unwrap SOL
│   Amount: {"__option":"Some","value":"1500000"}
│   From: CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3
│   To: DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy
│   Authority: 3Wnd5Df69KitZfUoPYZU438eFRNwGHkhLnSAWL65PxJX
batch(fallback only) Batch instructions
Intent + fields:
│ Batch instructions
│   Batched Instructions: [{"numberOfAccounts":6,"instructionData":["base64","AQIDBA=="]},{"numberOfAccounts":6,"instructionData":["base64","AQIDBA=="]}]

Associated token account program (3)

createAssociatedToken — Create associated token account 9WzDX…AWWM for 7dHbW…7ARj
Interpolated intent:
│ Create associated token account 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM for 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj

Intent + fields (fallback):
│ Create associated token account
│   Payer: HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH
│   Token Account: 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM
│   Owner: 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
│   Token Program: BPFLoaderUpgradeab1e11111111111111111111111
createAssociatedTokenIdempotent — Create associated token account 9WzDX…AWWM for 7dHbW…7ARj
Interpolated intent:
│ Create associated token account 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM for 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj

Intent + fields (fallback):
│ Create associated token account
│   Payer: HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH
│   Token Account: 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM
│   Owner: 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj
│   Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
│   Token Program: BPFLoaderUpgradeab1e11111111111111111111111
recoverNestedAssociatedToken — Recover nested token account 3Wnd5…PxJX to HN7cA…YWrH
Interpolated intent:
│ Recover nested token account 3Wnd5Df69KitZfUoPYZU438eFRNwGHkhLnSAWL65PxJX to HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH

Intent + fields (fallback):
│ Recover nested token account
│   Nested Token Account: 3Wnd5Df69KitZfUoPYZU438eFRNwGHkhLnSAWL65PxJX
│   Nested Mint: 4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T
│   Destination Token Account: HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH
│   Owner Token Account: Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS
│   Owner Mint: 7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj
│   Wallet: Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS
│   Token Program: BPFLoaderUpgradeab1e11111111111111111111111

@lorisleiva
lorisleiva requested a review from joncinque August 11, 2026 09:37

@joncinque joncinque left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

Sorry for the dumb question, but is there a mechanism for potentially substituting other labels for addresses?

For example, any time you see EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v you render USDC?

Also, up to you, but including the mint in the interpolated string for ATA creation could be nice

joncinque
joncinque approved these changes Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants