Skip to main content
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.

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.
Decode the hex-encoded public key material from the API response into raw bytes before constructing on-chain public key objects. Reject empty or missing key fields from the response.

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 fee payer to whichever signing account should pay cluster fees. Here, that’s the first signer’s public key.

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.
The recent blockhash is required to serialize the transaction unless you rely entirely on a durable nonce flow. If the first Fireblocks sign-only request is configured to apply a durable nonce, the platform can inject the nonce instruction and align the message with nonce state as part of processing. Serialization must allow missing signatures at this stage; otherwise the library refuses to output bytes before any signature exists.

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.
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.
Poll the transaction until it reaches the SIGNED state. Exit with an error if the platform reports FAILED, BLOCKED, or the poll exceeds your configured retry budget before you submit the next request.

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.
This chains the first signature into the same transaction bytes before the second vault signs. After this second signing step, the platform follows the usual broadcast path for a completed program call. To chain three or more vaults, mark every request as sign-only except the last.

Checklist

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. The console.log calls are included for development visibility; remove them when integrating into production code.