Use this page when your app does not sign Thru transactions directly in the first-party wallet UI. Typical external signer integrations include:Documentation Index
Fetch the complete documentation index at: https://docs.thru.org/llms.txt
Use this file to discover all available pages before exploring further.
- custody providers
- HSM or KMS-backed signing services
- backend transaction services
- custom wallet adapters
- embedded wallet providers
Mental Model
Treat the Thru transaction lifecycle as four separate steps:- Build the transaction payload.
- Hand the signing payload to the external signer.
- Receive the signed wire transaction back.
- Submit and track the transaction with Thru RPC.
Canonical Flow
For external signers, the recommended SDK flow is:- Build the transaction with
thru.transactions.build(...). - Produce a signing payload with
tx.toWireForSigning(). - Pass that serialized payload to your signer.
- Receive the signed wire transaction from the signer.
- Submit it with
thru.transactions.send(...)orthru.transactions.sendAndTrack(...).
signTransaction(serializedTransaction) contract used by Thru wallet integrations.
What The Signer Must Preserve
Once a signing payload has been produced, the signer should treat the transaction contents as fixed. In particular, do not mutate:- fee payer public key
- program public key
- account ordering
- instruction data
- nonce
- chain ID
- validity window fields such as
start_slotandexpiry_after - requested resource limits
Signing Contract
The current web wallet contract is:- input: base64-encoded serialized transaction payload
- output: signed base64-encoded wire transaction payload
fee_payer_signaturenoncestart_slotexpiry_afterfee_payer_pubkeyprogram_pubkeychain_id
Generic TypeScript Example
When To Use buildAndSign
Use transactions.buildAndSign(...) only when your signing implementation already lives inside your Thru wallet or SDK layer.
If the signer is outside that layer, prefer:
transactions.build(...)tx.toWireForSigning()- external
signTransaction(...) transactions.send(...)
Passkey-Managed Flows
Passkey-managed flows add an inner authorization step, but the outer signing model stays the same. The high-level sequence is:- Fetch or derive the wallet nonce.
- Build the trailing instruction payload you want the wallet to authorize.
- Create the passkey challenge from the nonce, ordered accounts, and trailing instruction bytes.
- Produce the
validateinstruction from the WebAuthn result. - Concatenate
validateand the trailing instruction payload. - Build the outer Thru transaction.
- Sign the outer transaction with the external signer.
- Submit the signed wire transaction.
Common Failure Modes
Watch for these issues when integrating an external signer:- stale fee payer nonce
- expired transaction validity window
- wrong chain ID
- mismatched fee payer key
- account reordering after the signing payload was generated
- modifying instruction data after the signer has already approved the payload
- confusing inner program authorization with the outer Thru transaction signature
Recommended Integration Boundary
For most teams, the cleanest split looks like this:- app or backend: resolves accounts, instruction bytes, fee payer, and validity rules
- external signer: signs the serialized transaction payload
- app or backend: submits the signed wire transaction and tracks status