DOCS / PROTOCOL / SIGNED-INTENT SPEC

Signed-intent spec

In progress

What a ChimpDAO card signs, in one place. Every authorisation is bound to one exact call: the contract, the function and its arguments (asset, amount, destination), the network, a nonce and an expiration.

Two kinds of signature

KINDUSED FORWHAT THE CHIP SIGNSREPLAY STOPS AT
Account authorisation
USED FOREvery payment and account operation, from the Main account or a card's wallet
WHAT THE CHIP SIGNSThe Stellar network's authorisation payload for the exact call
REPLAY STOPS ATNetwork nonce and expiration ledger
Presence attestation
USED FORCard registration only
WHAT THE CHIP SIGNSA digest of the registry call with a per-card nonce
REPLAY STOPS ATPer-card nonce in the registry

Account authorisation payload

PAYLOAD
digest = sha256( HashIdPreimage::SorobanAuthorization {
    network_id                  = sha256(network passphrase),
    nonce                       = random 64-bit integer,
    signature_expiration_ledger = current ledger + 120,   // about 10 minutes
    invocation                  = contract, function, arguments, sub-invocations
} )

An authorisation entry with the Protocol 28 credentials (sorobanCredentialsAddressV2) signs HashIdPreimageSorobanAuthorizationWithAddress instead: the same fields plus the authorising address. The client hashes the variant the entry carries and never converts it.

From a card's wallet, the chip's signature is placed in the entry as it is. From the Main account, it is wrapped as AccountAuth { key, auth }, which names the card that signed; the account checks the signature against that card's stored key. Nothing is hashed on top of the network's payload, and the contracts keep no nonce of their own: the network enforces the nonce and the expiration.

Presence attestation payload (registration)

PAYLOAD
digest = sha256( "chimpdao.nfc-nft.v3" ‖ XDR(registry address) ‖ XDR(Symbol "mint")
                 ‖ XDR(arguments) ‖ XDR(u32 nonce) )

nonce  = get_nonce(card public key) + 1

The domain string is specific to the card registry, so an attestation cannot be reused by another contract family. The arguments are the card's public key and the terms it is registered under, with an optional token URI.

Intents and their bound fields

Every intent signed by a card also binds the network, a nonce and an expiration ledger. Amounts are exact integers in the asset's smallest unit (7 decimal places on Stellar).

INTENTCALL SIGNEDBOUND FIELDSSTATUS
Transfer (send, move)
CALL SIGNEDtransfer(from, to, amount) on the asset contract
BOUND FIELDSAsset contract · amount · source (Main account or a card's wallet) · destination (G, C or M address)
STATUSLive on testnet
Swap (Soroswap)
CALL SIGNEDswap_exact_tokens_for_tokens(amount_in, min_out, path, to, deadline) on the router
BOUND FIELDSRouter contract · asset in and asset out (path) · amount in · minimum amount out, the "at least" figure shown · recipient, the signing account · deadline, 5 minutes · sub-call: transfer of the amount in to the pool pair
STATUSIn progress
Deposit into a DeFindex vault
CALL SIGNEDdeposit([amount], [min amount], from, invest = true) on the vault
BOUND FIELDSVault contract · amount (minimum = amount, single-asset vault) · depositor · sub-call: transfer of the amount to the vault. The vault's asset is checked before signing.
STATUSIn progress
Withdrawal from a DeFindex vault
CALL SIGNEDwithdraw(shares, [min amount out], from) on the vault
BOUND FIELDSVault contract · shares · minimum amount out (expected amount minus 0.5%) · account. Nothing is pulled from the signer.
STATUSIn progress
Card registration
CALL SIGNEDmint(attestation, public key, terms, token URI, nonce) on the registry
BOUND FIELDSRegistry contract · the card's public key, terms and optional token URI · per-card nonce (presence attestation)
STATUSLive on testnet
Funding a card from an external wallet
CALL SIGNEDtransfer(from, to, amount) on the XLM asset contract
BOUND FIELDSSigned by the sender's wallet, not by a card · source (the sender's address) · destination (a ChimpDAO Main account or card wallet, checked by the Fund page) · amount
STATUSLive on testnet

Swap, deposit and withdrawal are implemented in ChimpDAO's reference payment terminal on testnet. The iOS app does not sign them yet.

Who signs, and when

AUTHORISERORDERWHY
A card's wallet
ORDERDeclare, sign, then simulate. The client builds the authorisation itself; the chip signs it before any simulation.
WHYA recording simulation cannot run for a card's wallet that follows the shared code version (Protocol 28, CAP-85). A wrong declaration fails in simulation, and a signed authorisation can never cover more than it declares.
The Main account
ORDERSimulate, then sign. The simulation returns the entry the account must authorise; the chip signs it.
WHYThe entry is checked before the card signs (below).

The app's verification rule

Live on testnet

Before a card signs from the Main account, the iOS app checks that the entry returned by the simulation is exactly the requested call: same contract, same function, same arguments, no sub-call, and a single entry to sign for this account. Otherwise nothing is signed. An entry for another address is refused before any signature.

From a card's wallet the app builds the entry itself, so it is the requested call by construction. In both cases the review screen shows the network, the exact amount and the recipient before the card is tapped.

Test vectors

In progress

Automated tests in the contracts and reference-terminal repositories show each refusal below.

CASEREFUSED BYAUTOMATED TEST
Modified amount
REFUSED BYThe iOS app (exact-call check) and the signature itself
AUTOMATED TESTYes
Modified asset
REFUSED BYThe iOS app (exact-call check) and the signature itself
AUTOMATED TESTYes
Modified destination
REFUSED BYThe iOS app (exact-call check) and the signature itself
AUTOMATED TESTYes
Wrong target contract
REFUSED BYThe iOS app (exact-call check) and the signature itself
AUTOMATED TESTYes
Replayed authorisation
REFUSED BYThe Stellar network
AUTOMATED TESTYes
Expired authorisation
REFUSED BYThe Stellar network
AUTOMATED TESTYes
A removed card, or a card the account never listed
REFUSED BYThe account contract
AUTOMATED TESTYes

The iOS app refuses an entry for another address and builds the same bytes as the reference implementation. Test vectors published for the iOS app are the remaining item.

Edit this page in the repositoryCHIMPDAO DOCS · TESTNET