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.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.address('user-address'),
amount: number | bigintamount: function lamports(putativeLamports: bigint): LamportsThis 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.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.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.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.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): stringSerialize 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).serializeTransaction(const partiallySignedTx: Readonly<{
messageBytes: TransactionMessageBytes;
signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
partiallySignedTx);
var console: ConsoleThe `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
```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.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.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 & TransactionMessageWithLifetimeDecompile 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.decompileTransaction(const receivedTx: Readonly<{
messageBytes: TransactionMessageBytes;
signatures: SignaturesMap;
}> & TransactionWithBlockhashLifetime
receivedTx);
var console: ConsoleThe `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
```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.log('Decompiled transaction for inspection:');
var console: ConsoleThe `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
```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.log(' - Fee payer:', const decompiled: TransactionMessage & (TransactionMessageWithFeePayer<string> & TransactionMessageWithLifetime)decompiled.TransactionMessageWithFeePayer<TAddress extends string = string>.feePayer: Readonly<{
address: Address<string>;
}>
feePayer);
var console: ConsoleThe `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
```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.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: numberGets 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.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"> & stringA 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"> & stringA 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: ConsoleThe `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
```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.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.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.address('user-address'),
amount: number | bigintamount: function lamports(putativeLamports: bigint): LamportsThis 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.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.address('user-address'),
amount: number | bigintamount: function lamports(putativeLamports: bigint): LamportsThis 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.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.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.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.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): stringSerialize 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).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.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): LamportsThis 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.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.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.partiallySignTransaction(const transactionMessage: TransactionMessage & TransactionMessageWithFeePayer<string> & TransactionMessageWithBlockhashLifetimetransactionMessage);
// Serialize for transmission
const const serializedTx: stringserializedTx = const client: NosanaClientclient.NosanaClient.solana: SolanaServicesolana.SolanaService.serializeTransaction(transaction: Transaction): stringSerialize 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).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.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 & TransactionMessageWithLifetimeDecompile 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.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.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"> & stringA 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"> & stringA 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: ConsoleThe `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
```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.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.