Skip to content

Partial Signing Examples ​

Examples demonstrating how to build transactions where the backend partially signs a transaction and the user (fee payer) signs later.

Backend: Create and Partially Sign Transaction ​

ts
// Create an instruction that requires the backend signer
import { function address<TAddress extends string = string>(putativeAddress: TAddress): Address<TAddress>
Combines _asserting_ that a string is an address with _coercing_ it to the {@link Address } type. It's most useful with untrusted input.
@example```ts import { address } from '@solana/addresses'; await transfer(address(fromAddress), address(toAddress), lamports(100000n)); ``` > [!TIP] > When starting from a known-good address as a string, it's more efficient to typecast it rather than to use the {@link address} helper, because the helper unconditionally performs validation on its input. > > ```ts > import { Address } from '@solana/addresses'; > > const MEMO_PROGRAM_ADDRESS = > 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr' as Address<'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'>; > ```
address
} from '@nosana/kit';
const const transferInstruction: TransferSolInstruction<Address<"11111111111111111111111111111111">, string, "user-address", []>transferInstruction =
getTransferSolInstruction<string, "user-address", Address<"11111111111111111111111111111111">>(input: TransferSolInput<string, "user-address">, config?: {
    programAddress?: Address<"11111111111111111111111111111111"> | undefined;
} | undefined): TransferSolInstruction<Address<"11111111111111111111111111111111">, string, "user-address", []>
getTransferSolInstruction
({
source: TransactionSigner<string>source: const backendSigner: KeyPairSignerbackendSigner, destination: Address<"user-address">destination: address<"user-address">(putativeAddress: "user-address"): Address<"user-address">
Combines _asserting_ that a string is an address with _coercing_ it to the {@link Address } type. It's most useful with untrusted input.
@example```ts import { address } from '@solana/addresses'; await transfer(address(fromAddress), address(toAddress), lamports(100000n)); ``` > [!TIP] > When starting from a known-good address as a string, it's more efficient to typecast it rather than to use the {@link address} helper, because the helper unconditionally performs validation on its input. > > ```ts > import { Address } from '@solana/addresses'; > > const MEMO_PROGRAM_ADDRESS = > 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr' as Address<'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'>; > ```
address
('user-address'),
amount: number | bigintamount: function lamports(putativeLamports: bigint): Lamports
This helper combines _asserting_ that a number is a possible number of {@link Lamports } with _coercing_ it to the {@link Lamports } type. It's best used with untrusted input.
@example```ts import { lamports } from '@solana/rpc-types'; await transfer(address(fromAddress), address(toAddress), lamports(100000n)); ```
lamports
(1000n),
}); // Build the transaction with the USER's address as fee payer (not their signer!) // This sets up the transaction so the user will pay fees but sign later const const transactionMessage: TransactionMessage & TransactionMessageWithFeePayer<string> & TransactionMessageWithBlockhashLifetimetransactionMessage = await const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.
SolanaService.buildTransaction(instructions: Instruction | Instruction[], options?: {
    feePayer?: TransactionSigner | Address | string;
    estimateComputeUnits?: boolean;
}): Promise<TransactionMessage & TransactionMessageWithFeePayer & TransactionMessageWithBlockhashLifetime>
Build a transaction message from instructions. This function creates a transaction message with fee payer, blockhash, and instructions.
@paraminstructions Single instruction or array of instructions@paramoptions Optional configuration@paramoptions.feePayer Optional custom fee payer. Can be a TransactionSigner (for full signing) or an Address/string (for partial signing where feepayer signs later). Takes precedence over service feePayer and wallet.@paramoptions.estimateComputeUnits If true, estimates and sets the compute unit limit. Default: false.@returnsAn unsigned transaction message ready to be signed
buildTransaction
([const transferInstruction: TransferSolInstruction<Address<"11111111111111111111111111111111">, string, "user-address", []>transferInstruction], {
feePayer?: string | Address | TransactionSigner | undefinedfeePayer: address<"user-address">(putativeAddress: "user-address"): Address<"user-address">
Combines _asserting_ that a string is an address with _coercing_ it to the {@link Address } type. It's most useful with untrusted input.
@example```ts import { address } from '@solana/addresses'; await transfer(address(fromAddress), address(toAddress), lamports(100000n)); ``` > [!TIP] > When starting from a known-good address as a string, it's more efficient to typecast it rather than to use the {@link address} helper, because the helper unconditionally performs validation on its input. > > ```ts > import { Address } from '@solana/addresses'; > > const MEMO_PROGRAM_ADDRESS = > 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr' as Address<'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'>; > ```
address
('user-address'), // Just the address, not the signer
}); // Partially sign the transaction with embedded signers (backend's signer) // The fee payer (user) has NOT signed yet const
const partiallySignedTx: Readonly<{
    messageBytes: TransactionMessageBytes;
    signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
partiallySignedTx
= await const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.SolanaService.partiallySignTransaction(transactionMessage: TransactionMessage & TransactionMessageWithFeePayer & TransactionMessageWithBlockhashLifetime): Promise<Transaction & TransactionWithBlockhashLifetime>
Partially sign a transaction message with the signers embedded in the transaction. The transaction message must already have a fee payer address set (via buildTransaction with an address). Signers are extracted from instructions in the message (e.g., transfer source signer). Use this when building transactions where the fee payer will sign later.
@paramtransactionMessage The transaction message to sign (must have fee payer address set and signers embedded in instructions)@returnsA partially signed transaction
partiallySignTransaction
(const transactionMessage: TransactionMessage & TransactionMessageWithFeePayer<string> & TransactionMessageWithBlockhashLifetimetransactionMessage);
// Serialize for transmission to the user const const serializedTx: stringserializedTx = const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.SolanaService.serializeTransaction(transaction: Transaction): string
Serialize a transaction to a base64 string. Works with both partially signed and fully signed transactions. Use this to transmit transactions to other parties (e.g., for fee payer signing).
@paramtransaction The transaction to serialize@returnsBase64 encoded wire transaction string
serializeTransaction
(
const partiallySignedTx: Readonly<{
    messageBytes: TransactionMessageBytes;
    signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
partiallySignedTx
);
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v24.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v24.x/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
('Serialized transaction:', const serializedTx: stringserializedTx);

User: Receive, Verify, and Sign Transaction ​

ts
// Deserialize the received transaction
const 
const receivedTx: Readonly<{
    messageBytes: TransactionMessageBytes;
    signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
receivedTx
= await const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.SolanaService.deserializeTransaction(base64: string): Promise<Transaction & TransactionWithBlockhashLifetime>
Deserialize a base64 string back to a transaction. Use this to receive transactions from other parties. Note: This method automatically restores the `lastValidBlockHeight` metadata that is lost during serialization by fetching the latest blockhash from the RPC.
@parambase64 The base64 encoded transaction string@returnsThe deserialized transaction with restored lifetime metadata
deserializeTransaction
(const serializedTx: "base64-encoded-transaction-from-backend"serializedTx);
// (Optional) Decompile to inspect the transaction before signing const const decompiled: TransactionMessage & (TransactionMessageWithFeePayer<string> & TransactionMessageWithLifetime)decompiled = const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.SolanaService.decompileTransaction(transaction: Transaction): TransactionMessage & TransactionMessageWithFeePayer & TransactionMessageWithLifetime
Decompile a transaction back to a transaction message. Use this to inspect/verify the content of a deserialized transaction before signing. Note: Decompilation is lossy - some information like lastValidBlockHeight may not be fully reconstructed. The returned message is suitable for inspection but may not be suitable for re-signing without additional context.
@paramtransaction The compiled transaction to decompile@returnsThe decompiled transaction message (with either blockhash or durable nonce lifetime)
decompileTransaction
(
const receivedTx: Readonly<{
    messageBytes: TransactionMessageBytes;
    signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
receivedTx
);
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v24.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v24.x/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
('Decompiled transaction for inspection:');
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v24.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v24.x/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
(' - Fee payer:', const decompiled: TransactionMessage & (TransactionMessageWithFeePayer<string> & TransactionMessageWithLifetime)decompiled.
TransactionMessageWithFeePayer<TAddress extends string = string>.feePayer: Readonly<{
    address: Address<string>;
}>
feePayer
);
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v24.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v24.x/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
(' - Number of instructions:', const decompiled: TransactionMessage & (TransactionMessageWithFeePayer<string> & TransactionMessageWithLifetime)decompiled.TransactionMessageWithDurableNonceLifetime<TNonceAccountAddress extends string = string, TNonceAuthorityAddress extends string = string, TNonceValue extends string = string>.instructions: readonly InstructionWithoutLookupTables<string>[] | (readonly InstructionWithoutLookupTables<string>[] & readonly [AdvanceNonceAccountInstruction<string, string>, ...Instruction<string, readonly (AccountLookupMeta<string, string> | AccountMeta<string>)[]>[]]) | readonly Instruction<string, readonly (AccountLookupMeta<string, string> | AccountMeta<string>)[]>[] | (readonly Instruction<string, readonly (AccountLookupMeta<string, string> | AccountMeta<string>)[]>[] & readonly [...])instructions.ReadonlyArray<T>.length: number
Gets the length of the array. This is a number one higher than the highest element defined in an array.
length
);
// Verify the fee payer is the user's address if (const decompiled: TransactionMessage & (TransactionMessageWithFeePayer<string> & TransactionMessageWithLifetime)decompiled.
TransactionMessageWithFeePayer<TAddress extends string = string>.feePayer: Readonly<{
    address: Address<string>;
}>
feePayer
.address: Address<string>address !== const userSigner: KeyPairSigneruserSigner.address: NominalType<"brand", "Address"> & NominalType<"stringEncoding", "base58"> & stringaddress) {
throw new
var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error
('Fee payer mismatch!');
} // Sign with the user's signer (the fee payer) const
const fullySignedTx: FullySignedTransaction & TransactionWithinSizeLimit & Readonly<{
    messageBytes: TransactionMessageBytes;
    signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
fullySignedTx
= await const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.SolanaService.signTransactionWithSigners(transaction: Transaction & TransactionWithBlockhashLifetime, signers: TransactionPartialSigner[]): Promise<SendableTransaction & Transaction & TransactionWithBlockhashLifetime>
Sign a transaction with the provided signers. Use this when receiving a partially signed transaction that needs additional signatures. This adds signatures from the provided signers to the transaction.
@paramtransaction The transaction to sign (typically partially signed, received from another party)@paramsigners Array of TransactionPartialSigners to sign with@returnsThe signed transaction with additional signatures
signTransactionWithSigners
(
const receivedTx: Readonly<{
    messageBytes: TransactionMessageBytes;
    signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
receivedTx
, [const userSigner: KeyPairSigneruserSigner]);
// Send to the network import type { type Signature = NominalType<"brand", "Signature"> & NominalType<"stringEncoding", "base58"> & string
A 64-byte Ed25519 signature as a base58-encoded string.
Signature
} from '@solana/kit';
const const signature: Signaturesignature: type Signature = NominalType<"brand", "Signature"> & NominalType<"stringEncoding", "base58"> & string
A 64-byte Ed25519 signature as a base58-encoded string.
Signature
= await const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.
SolanaService.sendTransaction(transaction: SendableTransaction & Transaction & TransactionWithBlockhashLifetime, options?: {
    commitment?: "processed" | "confirmed" | "finalized";
}): Promise<Signature>
sendTransaction
(
const fullySignedTx: FullySignedTransaction & TransactionWithinSizeLimit & Readonly<{
    messageBytes: TransactionMessageBytes;
    signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
fullySignedTx
);
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v24.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v24.x/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
('Transaction sent:', const signature: Signaturesignature);

Multiple Backend Signers ​

ts
// Create instructions that require multiple backend signers
import { function address<TAddress extends string = string>(putativeAddress: TAddress): Address<TAddress>
Combines _asserting_ that a string is an address with _coercing_ it to the {@link Address } type. It's most useful with untrusted input.
@example```ts import { address } from '@solana/addresses'; await transfer(address(fromAddress), address(toAddress), lamports(100000n)); ``` > [!TIP] > When starting from a known-good address as a string, it's more efficient to typecast it rather than to use the {@link address} helper, because the helper unconditionally performs validation on its input. > > ```ts > import { Address } from '@solana/addresses'; > > const MEMO_PROGRAM_ADDRESS = > 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr' as Address<'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'>; > ```
address
} from '@nosana/kit';
const const instruction1: TransferSolInstruction<Address<"11111111111111111111111111111111">, string, "user-address", []>instruction1 =
getTransferSolInstruction<string, "user-address", Address<"11111111111111111111111111111111">>(input: TransferSolInput<string, "user-address">, config?: {
    programAddress?: Address<"11111111111111111111111111111111"> | undefined;
} | undefined): TransferSolInstruction<Address<"11111111111111111111111111111111">, string, "user-address", []>
getTransferSolInstruction
({
source: TransactionSigner<string>source: const backendSigner1: KeyPairSignerbackendSigner1, destination: Address<"user-address">destination: address<"user-address">(putativeAddress: "user-address"): Address<"user-address">
Combines _asserting_ that a string is an address with _coercing_ it to the {@link Address } type. It's most useful with untrusted input.
@example```ts import { address } from '@solana/addresses'; await transfer(address(fromAddress), address(toAddress), lamports(100000n)); ``` > [!TIP] > When starting from a known-good address as a string, it's more efficient to typecast it rather than to use the {@link address} helper, because the helper unconditionally performs validation on its input. > > ```ts > import { Address } from '@solana/addresses'; > > const MEMO_PROGRAM_ADDRESS = > 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr' as Address<'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'>; > ```
address
('user-address'),
amount: number | bigintamount: function lamports(putativeLamports: bigint): Lamports
This helper combines _asserting_ that a number is a possible number of {@link Lamports } with _coercing_ it to the {@link Lamports } type. It's best used with untrusted input.
@example```ts import { lamports } from '@solana/rpc-types'; await transfer(address(fromAddress), address(toAddress), lamports(100000n)); ```
lamports
(500n),
}); const const instruction2: TransferSolInstruction<Address<"11111111111111111111111111111111">, string, "user-address", []>instruction2 =
getTransferSolInstruction<string, "user-address", Address<"11111111111111111111111111111111">>(input: TransferSolInput<string, "user-address">, config?: {
    programAddress?: Address<"11111111111111111111111111111111"> | undefined;
} | undefined): TransferSolInstruction<Address<"11111111111111111111111111111111">, string, "user-address", []>
getTransferSolInstruction
({
source: TransactionSigner<string>source: const backendSigner2: KeyPairSignerbackendSigner2, destination: Address<"user-address">destination: address<"user-address">(putativeAddress: "user-address"): Address<"user-address">
Combines _asserting_ that a string is an address with _coercing_ it to the {@link Address } type. It's most useful with untrusted input.
@example```ts import { address } from '@solana/addresses'; await transfer(address(fromAddress), address(toAddress), lamports(100000n)); ``` > [!TIP] > When starting from a known-good address as a string, it's more efficient to typecast it rather than to use the {@link address} helper, because the helper unconditionally performs validation on its input. > > ```ts > import { Address } from '@solana/addresses'; > > const MEMO_PROGRAM_ADDRESS = > 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr' as Address<'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'>; > ```
address
('user-address'),
amount: number | bigintamount: function lamports(putativeLamports: bigint): Lamports
This helper combines _asserting_ that a number is a possible number of {@link Lamports } with _coercing_ it to the {@link Lamports } type. It's best used with untrusted input.
@example```ts import { lamports } from '@solana/rpc-types'; await transfer(address(fromAddress), address(toAddress), lamports(100000n)); ```
lamports
(500n),
}); // Build with user as fee payer const const transactionMessage: TransactionMessage & TransactionMessageWithFeePayer<string> & TransactionMessageWithBlockhashLifetimetransactionMessage = await const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.
SolanaService.buildTransaction(instructions: Instruction | Instruction[], options?: {
    feePayer?: TransactionSigner | Address | string;
    estimateComputeUnits?: boolean;
}): Promise<TransactionMessage & TransactionMessageWithFeePayer & TransactionMessageWithBlockhashLifetime>
Build a transaction message from instructions. This function creates a transaction message with fee payer, blockhash, and instructions.
@paraminstructions Single instruction or array of instructions@paramoptions Optional configuration@paramoptions.feePayer Optional custom fee payer. Can be a TransactionSigner (for full signing) or an Address/string (for partial signing where feepayer signs later). Takes precedence over service feePayer and wallet.@paramoptions.estimateComputeUnits If true, estimates and sets the compute unit limit. Default: false.@returnsAn unsigned transaction message ready to be signed
buildTransaction
([const instruction1: TransferSolInstruction<Address<"11111111111111111111111111111111">, string, "user-address", []>instruction1, const instruction2: TransferSolInstruction<Address<"11111111111111111111111111111111">, string, "user-address", []>instruction2], {
feePayer?: string | Address | TransactionSigner | undefinedfeePayer: address<"user-address">(putativeAddress: "user-address"): Address<"user-address">
Combines _asserting_ that a string is an address with _coercing_ it to the {@link Address } type. It's most useful with untrusted input.
@example```ts import { address } from '@solana/addresses'; await transfer(address(fromAddress), address(toAddress), lamports(100000n)); ``` > [!TIP] > When starting from a known-good address as a string, it's more efficient to typecast it rather than to use the {@link address} helper, because the helper unconditionally performs validation on its input. > > ```ts > import { Address } from '@solana/addresses'; > > const MEMO_PROGRAM_ADDRESS = > 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr' as Address<'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'>; > ```
address
('user-address'),
}); // Partially sign - both backend signers will sign const
const partiallySignedTx: Readonly<{
    messageBytes: TransactionMessageBytes;
    signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
partiallySignedTx
= await const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.SolanaService.partiallySignTransaction(transactionMessage: TransactionMessage & TransactionMessageWithFeePayer & TransactionMessageWithBlockhashLifetime): Promise<Transaction & TransactionWithBlockhashLifetime>
Partially sign a transaction message with the signers embedded in the transaction. The transaction message must already have a fee payer address set (via buildTransaction with an address). Signers are extracted from instructions in the message (e.g., transfer source signer). Use this when building transactions where the fee payer will sign later.
@paramtransactionMessage The transaction message to sign (must have fee payer address set and signers embedded in instructions)@returnsA partially signed transaction
partiallySignTransaction
(const transactionMessage: TransactionMessage & TransactionMessageWithFeePayer<string> & TransactionMessageWithBlockhashLifetimetransactionMessage);
// Serialize and send to user const const serializedTx: stringserializedTx = const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.SolanaService.serializeTransaction(transaction: Transaction): string
Serialize a transaction to a base64 string. Works with both partially signed and fully signed transactions. Use this to transmit transactions to other parties (e.g., for fee payer signing).
@paramtransaction The transaction to serialize@returnsBase64 encoded wire transaction string
serializeTransaction
(
const partiallySignedTx: Readonly<{
    messageBytes: TransactionMessageBytes;
    signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
partiallySignedTx
);

Complete Flow Example ​

This example shows the complete flow from backend to user:

ts
// ============================================================
// BACKEND SIDE: Create and partially sign a transaction
// ============================================================

import { function address<TAddress extends string = string>(putativeAddress: TAddress): Address<TAddress>
Combines _asserting_ that a string is an address with _coercing_ it to the {@link Address } type. It's most useful with untrusted input.
@example```ts import { address } from '@solana/addresses'; await transfer(address(fromAddress), address(toAddress), lamports(100000n)); ``` > [!TIP] > When starting from a known-good address as a string, it's more efficient to typecast it rather than to use the {@link address} helper, because the helper unconditionally performs validation on its input. > > ```ts > import { Address } from '@solana/addresses'; > > const MEMO_PROGRAM_ADDRESS = > 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr' as Address<'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'>; > ```
address
} from '@nosana/kit';
const const transferInstruction: TransferSolInstruction<Address<"11111111111111111111111111111111">, string, string, []>transferInstruction =
getTransferSolInstruction<string, string, Address<"11111111111111111111111111111111">>(input: TransferSolInput<string, string>, config?: {
    programAddress?: Address<"11111111111111111111111111111111"> | undefined;
} | undefined): TransferSolInstruction<Address<"11111111111111111111111111111111">, string, string, []>
getTransferSolInstruction
({
source: TransactionSigner<string>source: const backendSigner: KeyPairSignerbackendSigner, destination: Address<string>destination: const userSigner: KeyPairSigneruserSigner.address: NominalType<"brand", "Address"> & NominalType<"stringEncoding", "base58"> & stringaddress, amount: number | bigintamount: function lamports(putativeLamports: bigint): Lamports
This helper combines _asserting_ that a number is a possible number of {@link Lamports } with _coercing_ it to the {@link Lamports } type. It's best used with untrusted input.
@example```ts import { lamports } from '@solana/rpc-types'; await transfer(address(fromAddress), address(toAddress), lamports(100000n)); ```
lamports
(1000n),
}); // Build with user's address as fee payer (not their signer!) const const transactionMessage: TransactionMessage & TransactionMessageWithFeePayer<string> & TransactionMessageWithBlockhashLifetimetransactionMessage = await const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.
SolanaService.buildTransaction(instructions: Instruction | Instruction[], options?: {
    feePayer?: TransactionSigner | Address | string;
    estimateComputeUnits?: boolean;
}): Promise<TransactionMessage & TransactionMessageWithFeePayer & TransactionMessageWithBlockhashLifetime>
Build a transaction message from instructions. This function creates a transaction message with fee payer, blockhash, and instructions.
@paraminstructions Single instruction or array of instructions@paramoptions Optional configuration@paramoptions.feePayer Optional custom fee payer. Can be a TransactionSigner (for full signing) or an Address/string (for partial signing where feepayer signs later). Takes precedence over service feePayer and wallet.@paramoptions.estimateComputeUnits If true, estimates and sets the compute unit limit. Default: false.@returnsAn unsigned transaction message ready to be signed
buildTransaction
([const transferInstruction: TransferSolInstruction<Address<"11111111111111111111111111111111">, string, string, []>transferInstruction], {
feePayer?: string | Address | TransactionSigner | undefinedfeePayer: const userSigner: KeyPairSigneruserSigner.address: NominalType<"brand", "Address"> & NominalType<"stringEncoding", "base58"> & stringaddress, }); // Partially sign with backend's signer const
const partiallySignedTx: Readonly<{
    messageBytes: TransactionMessageBytes;
    signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
partiallySignedTx
:
type Transaction = {
    readonly messageBytes: TransactionMessageBytes;
    readonly signatures: SignaturesMap;
}
Transaction
&
type TransactionWithBlockhashLifetime = {
    readonly lifetimeConstraint: TransactionBlockhashLifetime;
}
A transaction whose lifetime is determined by the age of a blockhash observed on the network. The transaction will continue to be eligible to land until the network considers the `blockhash` to be expired.
TransactionWithBlockhashLifetime
=
await const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.SolanaService.partiallySignTransaction(transactionMessage: TransactionMessage & TransactionMessageWithFeePayer & TransactionMessageWithBlockhashLifetime): Promise<Transaction & TransactionWithBlockhashLifetime>
Partially sign a transaction message with the signers embedded in the transaction. The transaction message must already have a fee payer address set (via buildTransaction with an address). Signers are extracted from instructions in the message (e.g., transfer source signer). Use this when building transactions where the fee payer will sign later.
@paramtransactionMessage The transaction message to sign (must have fee payer address set and signers embedded in instructions)@returnsA partially signed transaction
partiallySignTransaction
(const transactionMessage: TransactionMessage & TransactionMessageWithFeePayer<string> & TransactionMessageWithBlockhashLifetimetransactionMessage);
// Serialize for transmission const const serializedTx: stringserializedTx = const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.SolanaService.serializeTransaction(transaction: Transaction): string
Serialize a transaction to a base64 string. Works with both partially signed and fully signed transactions. Use this to transmit transactions to other parties (e.g., for fee payer signing).
@paramtransaction The transaction to serialize@returnsBase64 encoded wire transaction string
serializeTransaction
(
const partiallySignedTx: Readonly<{
    messageBytes: TransactionMessageBytes;
    signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
partiallySignedTx
);
// ============================================================ // USER SIDE: Receive, verify, sign, and send // ============================================================ // Deserialize the received transaction const
const receivedTx: Readonly<{
    messageBytes: TransactionMessageBytes;
    signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
receivedTx
= await const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.SolanaService.deserializeTransaction(base64: string): Promise<Transaction & TransactionWithBlockhashLifetime>
Deserialize a base64 string back to a transaction. Use this to receive transactions from other parties. Note: This method automatically restores the `lastValidBlockHeight` metadata that is lost during serialization by fetching the latest blockhash from the RPC.
@parambase64 The base64 encoded transaction string@returnsThe deserialized transaction with restored lifetime metadata
deserializeTransaction
(const serializedTx: stringserializedTx);
// (Optional) Inspect before signing const const decompiled: TransactionMessage & (TransactionMessageWithFeePayer<string> & TransactionMessageWithLifetime)decompiled = const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.SolanaService.decompileTransaction(transaction: Transaction): TransactionMessage & TransactionMessageWithFeePayer & TransactionMessageWithLifetime
Decompile a transaction back to a transaction message. Use this to inspect/verify the content of a deserialized transaction before signing. Note: Decompilation is lossy - some information like lastValidBlockHeight may not be fully reconstructed. The returned message is suitable for inspection but may not be suitable for re-signing without additional context.
@paramtransaction The compiled transaction to decompile@returnsThe decompiled transaction message (with either blockhash or durable nonce lifetime)
decompileTransaction
(
const receivedTx: Readonly<{
    messageBytes: TransactionMessageBytes;
    signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
receivedTx
);
// Verify fee payer if (const decompiled: TransactionMessage & (TransactionMessageWithFeePayer<string> & TransactionMessageWithLifetime)decompiled.
TransactionMessageWithFeePayer<TAddress extends string = string>.feePayer: Readonly<{
    address: Address<string>;
}>
feePayer
.address: Address<string>address !== const userSigner: KeyPairSigneruserSigner.address: NominalType<"brand", "Address"> & NominalType<"stringEncoding", "base58"> & stringaddress) {
throw new
var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error
('Fee payer mismatch!');
} // Sign with user's signer const
const fullySignedTx: FullySignedTransaction & TransactionWithinSizeLimit & Readonly<{
    messageBytes: TransactionMessageBytes;
    signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
fullySignedTx
= await const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.SolanaService.signTransactionWithSigners(transaction: Transaction & TransactionWithBlockhashLifetime, signers: TransactionPartialSigner[]): Promise<SendableTransaction & Transaction & TransactionWithBlockhashLifetime>
Sign a transaction with the provided signers. Use this when receiving a partially signed transaction that needs additional signatures. This adds signatures from the provided signers to the transaction.
@paramtransaction The transaction to sign (typically partially signed, received from another party)@paramsigners Array of TransactionPartialSigners to sign with@returnsThe signed transaction with additional signatures
signTransactionWithSigners
(
const receivedTx: Readonly<{
    messageBytes: TransactionMessageBytes;
    signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
receivedTx
, [const userSigner: KeyPairSigneruserSigner]);
// Send to network import type { type Signature = NominalType<"brand", "Signature"> & NominalType<"stringEncoding", "base58"> & string
A 64-byte Ed25519 signature as a base58-encoded string.
Signature
} from '@solana/kit';
const const signature: Signaturesignature: type Signature = NominalType<"brand", "Signature"> & NominalType<"stringEncoding", "base58"> & string
A 64-byte Ed25519 signature as a base58-encoded string.
Signature
= await const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.
SolanaService.sendTransaction(transaction: SendableTransaction & Transaction & TransactionWithBlockhashLifetime, options?: {
    commitment?: "processed" | "confirmed" | "finalized";
}): Promise<Signature>
sendTransaction
(
const fullySignedTx: FullySignedTransaction & TransactionWithinSizeLimit & Readonly<{
    messageBytes: TransactionMessageBytes;
    signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
fullySignedTx
);
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v24.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v24.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v24.x/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v24.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
('Transaction confirmed:', const signature: Signaturesignature);

TIP

When to use partial signing:

  • Backend needs to sign instructions (e.g., transferring from backend-controlled accounts)
  • User should pay transaction fees
  • You want to separate backend and user signing responsibilities
  • You need to serialize/transmit transactions between services

WARNING

Always verify the transaction contents (using decompileTransaction) before signing on the user side to ensure the transaction matches expectations.