Transactions Overview
The build → sign → simulate → send → track flow shared by EVM and Solana, driven from an Address with buildTransaction(), signTransaction(), simulateTransaction() and sendTransaction().
Every value transfer and contract call in wative-core moves through one lifecycle: build → sign → simulate → send → track. The same four verbs work on EVM and Solana — you drive them from an Address (or its per-chain wallet.evm / wallet.svm view), which owns the key and the Network. The shapes differ per chain, but the steps and the method names do not.
The lifecycle
Build — address.buildTransaction(params) returns a concrete EvmTransaction or SvmTransaction. This is a structural, offline step: no network call is made.
Sign — address.signTransaction(tx) (or await tx.sign()) produces the signed bytes. Signing is local ECDSA/ed25519; it touches the network only to auto-fill fields you left out (EVM nonce/gas/fees, Solana recentBlockhash).
Simulate (optional) — address.simulateTransaction(tx) pre-flights the transaction (eth_call + gas estimate on EVM, simulateTransaction on Solana) and resolves a SimulationResult. It never broadcasts.
Send — address.sendTransaction(tx) broadcasts and returns a TransactionTracker synchronously. It signs first if the transaction is not yet signed.
Track — await the tracker's lifecycle promises (whenSubmitted(), whenMined(), whenConfirmed()) or subscribe to its change / confirmed / failed events.
import { Workspace } from "wative-core";
const ws = await Workspace.open({ path, password: "wsp-pwd" });
const acc = await ws.accounts.create("Desk", "wsp-pwd", MNEMONIC);
const evm = acc.wallets[0].evm; // an EvmSigner (an Address view)
// 1 — build (offline; nonce/gas fill in at sign time)
const tx = await evm.buildTransaction({
to: "0x1234567890123456789012345678901234567890",
value: 1_000_000_000_000_000n,
chainId: 1,
});
// 2 — simulate (optional pre-flight, RPC)
const sim = await evm.simulateTransaction(tx);
if (!sim.success) throw new Error(sim.error);
// 3 — send + track (RPC)
const tracker = evm.sendTransaction(tx);
const hash = await tracker.whenSubmitted();
const receipt = await tracker.whenConfirmed();Installation, if you have not added the package yet:
pnpm add wative-corenpm install wative-coreOffline vs RPC steps
Building is always offline. Only signing, simulating, sending, and tracking reach the network — and even signing is fully offline when you supply every field yourself.
| Step | Method | Reaches the network? |
|---|---|---|
| Build | buildTransaction() | No — structural only |
| Sign | signTransaction() / tx.sign() | Only to auto-fill omitted fields (EVM nonce/gas/fees, SVM recentBlockhash); fully offline if you supply them |
| Simulate | simulateTransaction() | Yes |
| Send | sendTransaction() | Yes |
| Track | TransactionTracker promises/events | Yes (polls the node) |
A transaction can be built and signed entirely offline. On EVM, pass nonce, gasLimit, and the fee fields; on Solana, pass recentBlockhash. The signer then never calls out. See EVM Transactions and Solana Transactions.
The Transaction type
Transaction is the polymorphic interface both chains implement. EvmTransaction and SvmTransaction are the concrete classes — you can think of a built transaction as EvmTransaction | SvmTransaction.
The interface declares only the members that are identical across chains:
| Member | Type | Notes |
|---|---|---|
vm | VmToken | "evm" or "svm" — discriminates the union |
status | TransactionStatus | "draft" → … → "confirmed" / "failed" (see Tracking & Lifecycle) |
sign() | Promise<this> | this | Idempotent; caches the signed bytes |
send() | TransactionTracker | Signs first if needed |
simulate() | Promise<SimulationResult> | Pre-flight only |
Per-chain members such as toRawTx(), hash, and from live on the concrete classes and are intentionally not on the interface — hold the concrete EvmTransaction / SvmTransaction type (which buildTransaction() returns) to reach them. See External Signers & Raw Tx.
Driving it from an Address
The four *Transaction helpers on an Address are thin, ownership-checked delegates — each throws PARAMETER_ERROR if tx.from does not match the address's own public key.
| Method | Returns | Delegates to |
|---|---|---|
buildTransaction(params) | EvmTransaction / SvmTransaction | the chain dialect |
signTransaction(tx) | the same tx (generic) | tx.sign() |
simulateTransaction(tx) | Promise<SimulationResult> | tx.simulate() |
sendTransaction(tx) | TransactionTracker | tx.send() |
signTransaction starts signing fire-and-forget and returns the transaction synchronously. To observe completion or failure, await tx.sign() (idempotent), read tx.status ("failed" on a signing error), or just call sendTransaction(), which signs for you.