Skip to content

✨ [CW-28621] feat(ada): support putting token assets into change utxo for all tx types#1152

Merged
a0979470582 merged 1 commit into
masterfrom
feat/ada-transfer-with-token-change
Jun 26, 2026
Merged

✨ [CW-28621] feat(ada): support putting token assets into change utxo for all tx types#1152
a0979470582 merged 1 commit into
masterfrom
feat/ada-transfer-with-token-change

Conversation

@a0979470582

@a0979470582 a0979470582 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Lets users spend ADA from UTXOs that contain Cardano native tokens. Previously these transactions failed (ValueNotConservedUTxO) because the change output couldn't carry the tokens back. This makes every ADA transaction type's change output token-aware through a single unified encoding — no dedicated token tx type.

  • Each change output carries a pre-encoded CBOR value: a bare uint for ADA-only change, or 82 <lovelace> <multiasset> when the change holds native tokens (canonically ordered — keys sorted by length then bytewise)
  • New ChangeOutput type (the change field + optional assets?: TokenAsset[]); the app passes structured token data, the SDK does the CBOR encoding
  • Single getChangeArgument (value-blob, 292 bytes) shared by Transfer and all six staking types; pure-ADA tx bodies stay byte-identical, so existing signatures and flows are unaffected
  • Paired card-side change: coolwallet-signing-tools#201

Design notes

  • Superseded approach: first built as a dedicated TransferWithTokenChange tx type to isolate risk, then merged into the existing types — the token change layout is a strict superset of pure ADA, so unifying drops a whole type and removes drift between the token and non-token paths.
  • 6AE2 on-card throw: the change-value length field originally used setBufferInt(.., 0, 200), but the card's 1-byte range comparator can't take an upper bound ≥ 128, so it threw for every value. Switched to setBufferIntUnsafe (the SDK already caps the value at 200 bytes in getChangeArgument).
  • Canonical CBOR: multi-asset map keys must be sorted (policy id bytewise; asset name by byte length then bytewise) or the node rejects the tx — buildMultiAssetCbor enforces this.

Status — verified on real card ✅

  • All 7 scripts regenerated for the unified encoding and re-signed with CBPubKey
  • Signing matrix test: every tx type × {without token, with token}, all green on a real card with no -u
  • The two preserved pure-ADA baselines (StakeRegisterAndDelegate / Abstain) pass unchanged → confirms pure-ADA tx bodies are byte-identical under the new encoding (zero regression to existing flows)
  • getTransactionSize covered for the token path (app fee estimation); every with-token case is a consistent +80 bytes over its no-token pair

Remaining (out of this PR's scope)

  • Testnet acceptance: submit one real value-conserving token tx on preprod/preview to confirm end-to-end on-chain (validates minADA, value/token conservation, canonical ordering together)
  • App side (handled separately, see app PR #10212): minADA calculation, enumerating every token in the selected input UTXOs, value conservation

Known limit: the change value blob is capped at 200 bytes (~5 tokens); beyond that the SDK throws in JS rather than emitting a malformed tx.

🤖 Generated with Claude Code

PR Summary by Typo

Overview

This PR introduces support for including token assets in change UTXOs across all Cardano (ADA) transaction types. This enhancement allows for more complex transactions involving native tokens to be handled correctly.

Key Changes

  • Updated package version to 2.0.2.
  • Modified transaction scripts and signatures for various ADA transaction types (e.g., Transfer, StakeRegister, Delegate) to accommodate token assets.
  • Introduced new TokenAsset and ChangeOutput interfaces, and updated RawTransaction to utilize ChangeOutput for handling token-bearing change UTXOs.
  • Implemented buildMultiAssetCbor and encodeOutputValue utilities to ensure correct and canonical CBOR encoding of multi-asset values.
  • Expanded transaction tests to cover scenarios with and without token assets in change outputs, and added dedicated tests for multi-asset CBOR encoding.

Work Breakdown

Category Lines Changed
New Work 328 (76.6%)
Churn 23 (5.4%)
Rework 77 (18.0%)
Total Changes 428
To turn off PR summary, please visit Notification settings.

@a0979470582 a0979470582 changed the title feat(ada): add TransferWithTokenChange for token-bearing UTXOs refactor(ada): unified change encoding (token-bearing UTXO support) Jun 25, 2026
@a0979470582 a0979470582 changed the title refactor(ada): unified change encoding (token-bearing UTXO support) ✨ [CW-28621] feat(ada): support transfer and staking with token change Jun 25, 2026
@a0979470582 a0979470582 force-pushed the feat/ada-transfer-with-token-change branch from afd9524 to d029567 Compare June 25, 2026 12:46
@a0979470582 a0979470582 changed the title ✨ [CW-28621] feat(ada): support transfer and staking with token change feat(ada): support spending ADA from token-bearing UTXOs Jun 25, 2026
@a0979470582 a0979470582 force-pushed the feat/ada-transfer-with-token-change branch from d029567 to cebad1e Compare June 25, 2026 12:56
@a0979470582 a0979470582 changed the title feat(ada): support spending ADA from token-bearing UTXOs ✨ [CW-28621] feat(ADA): support putting token assets into change utxo for all tx types Jun 26, 2026
@a0979470582 a0979470582 changed the title ✨ [CW-28621] feat(ADA): support putting token assets into change utxo for all tx types ✨ [CW-28621] feat(ada): support putting token assets into change utxo for all tx types Jun 26, 2026
@a0979470582 a0979470582 force-pushed the feat/ada-transfer-with-token-change branch 4 times, most recently from 63ff148 to 995c157 Compare June 26, 2026 06:01
@a0979470582 a0979470582 marked this pull request as ready for review June 26, 2026 06:11
@a0979470582 a0979470582 enabled auto-merge June 26, 2026 06:11
@typo-app

typo-app Bot commented Jun 26, 2026

Copy link
Copy Markdown

Static Code Review 📊

✅ All quality checks passed!

@cbx-tech cbx-tech requested review from a team, Dorac, Rickuan, gundomlegend, hankliu418, jin860315, melody0000tw and tp26610 and removed request for a team June 26, 2026 06:11

@typo-app typo-app Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

AI Code Review 🤖

Files Reviewed: 10
Comments Added: 4
Lines of Code Analyzed: 505
Critical Issues: 0

PR Health: Good

Give 👍 or 👎 on each review comment to help us improve.

Comment thread packages/coin-ada/src/utils/transactionUtil.ts Outdated
Comment thread packages/coin-ada/src/utils/transactionUtil.ts Outdated
hankliu418
hankliu418 previously approved these changes Jun 26, 2026
@a0979470582 a0979470582 marked this pull request as draft June 26, 2026 06:21
auto-merge was automatically disabled June 26, 2026 06:21

Pull request was converted to draft

@a0979470582 a0979470582 force-pushed the feat/ada-transfer-with-token-change branch from 995c157 to c37eec3 Compare June 26, 2026 06:23
Comment thread packages/coin-ada/src/utils/transactionUtil.ts Outdated
When a user's UTXO holds native tokens, the change output must carry
those tokens back or the node rejects the tx (ValueNotConservedUTxO).
This makes every ADA tx type's change output token-aware through a
single unified value-blob encoding, so a token-bearing UTXO can be
spent without a dedicated tx type.

- Each change output carries a pre-encoded CBOR value: a bare uint for
  ADA-only change, or `82 <lovelace> <multiasset>` when the change holds
  native tokens, canonically ordered (keys sorted by length then bytewise)
- New ChangeOutput type (change field + optional assets: TokenAsset[]);
  the app passes structured token data, the SDK does the CBOR encoding
- Single getChangeArgument (value-blob, 292 bytes) shared by Transfer and
  all six staking types; pure-ADA tx bodies stay byte-identical, so
  existing signatures and flows are unaffected
- Card-side scripts regenerated in coolwallet-signing-tools (value-blob
  change + setBufferIntUnsafe for the >127-byte length field) and
  re-signed with CBPubKey
- Signing matrix test: every tx type, with and without token, verified
  end-to-end on a real card; preserved pure-ADA baselines pass unchanged

Paired with coolwallet-signing-tools (AdaScript unified change encoding).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread packages/coin-ada/src/utils/transactionUtil.ts
@a0979470582 a0979470582 marked this pull request as ready for review June 26, 2026 06:43
@cbx-tech cbx-tech requested review from a team June 26, 2026 06:43
@a0979470582 a0979470582 enabled auto-merge June 26, 2026 06:44
@a0979470582 a0979470582 merged commit 6816bb3 into master Jun 26, 2026
3 checks passed
@a0979470582 a0979470582 deleted the feat/ada-transfer-with-token-change branch June 26, 2026 06:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants