Solana Transactions
SvmTransaction and SvmTxBuildParams — building from recipient/amount/instructions, supplying recentBlockhash for a fully offline build, and passing @solana/web3.js instructions.
SvmTransaction is the concrete Solana transaction. As on EVM, the common path is address.buildTransaction(params), which binds the address so the transaction can sign, send, and simulate. The underlying @solana/web3.js Transaction is built lazily — on the first sign(), simulate(), or toRawTx() — so constructing one is always offline.
Constructing an SvmTransaction
Both an object form and a positional form (from, recipient, amount, opts?) are supported.
import { SvmTransaction } from "wative-core";
const tx = new SvmTransaction({
from: "SenderPubkey…",
recipient: "RecipientPubkey…",
amount: 5000n, // lamports (raw base units)
});Built from an address, a native SOL transfer is just recipient + amount:
const svm = acc.wallets[0].svm; // an SvmSigner
const tx = svm.buildTransaction({
recipient: "RecipientPubkey…",
amount: 5000n,
});SvmTxBuildParams
| Field | Type | Notes |
|---|---|---|
recipient | string | base58 destination for the default native transfer |
amount | bigint | lamports; coerced, minimum 0 |
tokenMint | string | unimplemented convenience param — throws; route SPL through instructions (see Token Transfers) |
tokenProgram | string | token program id for SPL flows |
computeUnitLimit | number | emits a ComputeBudget setComputeUnitLimit instruction |
computeUnitPrice | bigint | priority fee (micro-lamports); auto-defaulted when omitted and an address is bound |
recentBlockhash | string | supply for a fully offline build (see below) |
feePayer | string | defaults to from; see fee-payer rules |
rpcUrl | string | per-transaction endpoint override; validated |
instructions | ReadonlyArray<unknown> | arbitrary instructions; overrides the default native transfer |
addressLookupTables | ReadonlyArray<string> | see the lookup-table note below |
memo | string | appended as an SPL Memo instruction |
instructions
When you pass a non-empty instructions array, it replaces the default native SOL transfer — the builder adds your instructions instead. Two shapes are accepted:
- Real
@solana/web3.jsTransactionInstructioninstances (e.g. fromSystemProgram.transfer(...)), passed through unchanged. - The library's plain instruction shape — an object with
programId(base58string),accounts([{ pubkey, isSigner, isWritable }]), anddata(Uint8Array) — which the builder converts to a realTransactionInstruction.
import { SystemProgram, PublicKey } from "@solana/web3.js";
const ix = SystemProgram.transfer({
fromPubkey: new PublicKey(sender),
toPubkey: new PublicKey(recipient),
lamports: 5000n,
});
const tx = svm.buildTransaction({ recipient, amount: 0n, instructions: [ix] });Any computeUnitLimit / computeUnitPrice ComputeBudget instructions are prepended, and a memo, if set, is appended — around whatever instructions you supply.
Fully offline build with recentBlockhash
Without a recentBlockhash, the transaction fetches one over RPC through its bound address the first time it is built (at sign / simulate / toRawTx). Supply one yourself and the build is purely structural — no address and no network call are needed, which is exactly what makes an offline toRawTx() handoff possible.
const tx = new SvmTransaction({
from: "SenderPubkey…",
recipient: "RecipientPubkey…",
amount: 5000n,
recentBlockhash: "9xQe…recentBlockhash",
});
const built = await tx.toRawTx(); // @solana/web3.js Transaction, no RPCWhen the library fetches the blockhash it also records the block height past which it expires, so an expired transaction is reported as dropped promptly instead of waiting out the poll budget. A blockhash you supply carries no such height — its expiry is yours to track.
Fee-payer rules
feePayer defaults to from. It accepts a base58 string, a PublicKey, or raw key bytes, and is resolved to canonical base58 at construction; null is treated the same as omitting it (use from).
sign() can only produce the from account's signature. A feePayer distinct from from is therefore refused as a precondition, before an unsignable transaction is built:
// feePayer !== from
await tx.sign();
// PARAMETER_ERROR: SvmTransaction.sign cannot sign a distinct feePayer; it must
// equal from or be undefined. For an external fee payer, use toRawTx() and sign
// it yourself.For a sponsored / relayer flow where someone else pays the fee, use toRawTx() to get the underlying @solana/web3.js Transaction and have the external fee payer sign it.
Memo and lookup tables
memo— a non-emptymemois appended as an SPL Memo-program instruction, so the note actually lands on-chain rather than being dropped.addressLookupTables— Address Lookup Tables require a versioned (v0) transaction, and this builder produces legacy transactions only. A non-emptyaddressLookupTablesis rejected loudly withUNSUPPORTED_OPrather than being silently ignored.