Collect signatures from multiple Fireblocks vaults on a single Solana transaction by chaining program call requests. The first vault signs in sign-only mode, the platform returns a partially signed payload, and each subsequent vault signs that same payload in turn. The transaction bytes accumulate each signature until the last request follows the normal broadcast path. This pattern fits workflows where two or more vault-owned keys must sign the same message — for example, multisig-style arrangements or instructions that list multiple signers. The walkthrough below uses TypeScript with Solana’s JavaScript client and the Fireblocks TypeScript SDK as a concrete illustration. The same sequence of API calls and transaction fields applies if you use another language, generated client, or raw HTTPS to Fireblocks and your Solana RPC.Documentation Index
Fetch the complete documentation index at: https://developers.fireblocks.com/llms.txt
Use this file to discover all available pages before exploring further.
What you need
- Fireblocks API credentials, loaded from a secret manager. Never commit API keys or signing keys to source control.
- A Solana library or RPC client that can build transactions, set the fee payer and recent blockhash (or nonce), serialize a partially signed message, and submit to the network.
- REST or SDK access to the Fireblocks transactions endpoints, with support for Solana program call parameters.
- Vault accounts whose on-chain addresses match every signer you attach to the transaction.
Resolve each signer’s Solana public key
For each vault that will sign, resolve its Solana public key through Fireblocks using the vault account identifier, asset ID, and derivation parameters your workspace uses in production, so the keys match what Policies and routing expect.Build the instruction so every Fireblocks signer appears in the message
A typical transfer-style instruction marks only the sender as a signer. When two different keys must sign the same instruction, each of their public keys must appear in the instruction’s account list with the signer flag set. If an address is not a signer in the compiled message, neither the chain nor Fireblocks treats it as required for that transaction. The snippet uses a simple system transfer and marks the second party as an additional signer for illustration only — replace accounts and program layout with your real instruction.Set the recent blockhash and serialize for partial signing
Open an RPC session to your Solana cluster and fetch a recent blockhash. If you use a durable nonce workflow, fetch the nonce instead.Durable nonce timing
A durable nonce replaces the ephemeral recent blockhash so the transaction can stay valid while signing and Policy approval finish. A standard recent blockhash on Solana typically expires after roughly 60–90 seconds, which is often too short for multi-vault signing unless you use a nonce.Warning: When the first sign-only request opts into durable nonce handling, the partially signed payload is tied to that nonce account state. If the consuming transaction is not broadcast within roughly 10 minutes of the first request reaching a fully signed state, the platform may advance the nonce and the bytes you hold become invalid. Refresh nonce state and repeat signing from the first signer when that happens.
First Fireblocks request: Sign only with the first vault
Create a Fireblocks transaction whose operation is a Solana program call, attach the base64-encoded partial transaction as the program-call payload, and mark the request as sign-only so the first vault adds its signature without treating this step as the final on-chain send.Second Fireblocks request: Reuse the partially signed payload from the first response
After the first request reaches the SIGNED state, read the returned partially signed program-call payload from the transaction details endpoint. That blob already includes platform-side adjustments (such as durable nonce handling) and the first signature. Submit it again as the program-call payload on a new request whose source is the vault that owns the next missing signer. Some client libraries omit this response field from generated models; treat it as opaque base64 from the API if your typings do not list it yet.Checklist
| Detail | Why it matters |
|---|---|
| Every co-signing address is marked as a signer in the compiled message. | Otherwise only one signer appears on chain, and additional vaults have nothing valid to sign. |
| The fee payer is a signing account you control. | The fee payer must sign the transaction. Keep routing consistent with which vault pays fees. |
| Client serialization allows missing signatures at intermediate steps. | Otherwise the library refuses to output bytes before signing. |
| Intermediate Fireblocks requests are sign-only. | This keeps middle steps as “add signature” rather than final submission. |
| Each follow-up request reuses the updated program-call payload field from the API response. | This guarantees each vault signs the same message, including prior signatures. |
| The source vault on each Fireblocks request matches the next missing signer. | Signing order must follow the message’s required signature slots. |
End-to-end example
The listing below is a single-file TypeScript illustration you can copy into your own project layout. Load API credentials and signing material from your environment or secret manager — never embed them in source control. Theconsole.log calls are included for development visibility; remove them when integrating into production code.