import { createSolanaRpc, createSolanaRpcSubscriptions, Address, Signature, Instruction, TransactionMessageWithBlockhashLifetime, sendAndConfirmTransactionFactory, TransactionMessageWithFeePayer, TransactionMessageWithLifetime, TransactionMessage, SendableTransaction, Transaction, BaseTransactionMessage, TransactionSigner, TransactionWithBlockhashLifetime, TransactionPartialSigner } from '@solana/kit'; import { estimateComputeUnitLimitFactory } from '@solana-program/compute-budget'; import { getCreateAssociatedTokenIdempotentInstructionAsync } from '@solana-program/token'; import { getTransferSolInstruction } from '@solana-program/system'; import { Logger } from '../../logger/Logger.js'; import { Wallet } from '../../types.js'; import { SolanaConfig } from '../../config/types.js'; /** * Factory function to create an estimateAndSetComputeUnitLimit function * that estimates compute units and adds the set compute unit limit instruction */ declare function estimateAndSetComputeUnitLimitFactory(...params: Parameters): (transactionMessage: T) => Promise, "instructions"> & { readonly instructions: readonly [...T["instructions"], import("@solana-program/compute-budget").SetComputeUnitLimitInstruction]; } : never>; /** * Dependencies for SolanaService * @group @nosana/kit */ export interface SolanaServiceDeps { logger: Logger; getWallet: () => Wallet | undefined; } export interface BalanceInfo { owner: Address; mint: Address | 'SOL'; amount: bigint; decimals: number; uiAmount: number; } export interface SolBalanceInfo extends BalanceInfo { mint: 'SOL'; } /** * Result of sending a single transaction within a batch. * @group @nosana/kit */ export interface BatchTransactionResult { /** Whether the transaction was sent and confirmed successfully. */ status: 'fulfilled' | 'rejected'; /** Convenience flag: `true` when `status` is `'fulfilled'`. */ confirmed: boolean; /** The transaction signature, present when `status` is `'fulfilled'`. */ signature?: Signature; /** The error that occurred, present when `status` is `'rejected'`. */ error?: unknown; /** The instructions that made up this transaction (the packed bucket). */ instructions: Instruction[]; /** * The indices of the original `groups` this transaction carried, in order — the * bridge back from a per-transaction result to the per-group input you submitted. * For example `groupIndices: [6, 7]` means this transaction packed `groups[6]` * and `groups[7]`, so both share this transaction's `status`/`signature`/`error`. * When every group is a single instruction, `groupIndices[k]` corresponds to * `instructions[k]`. */ groupIndices: number[]; } /** * One packed, signed, but **un-sent** transaction produced by * {@link SolanaService.buildAndSignBatch}. The `blob` can be persisted and * broadcast later by a separate process, which is what enables persist-before-send * idempotency (a crash mid-send can replay the identical signed transaction, and * the chain dedups it by signature). * @group @nosana/kit */ export interface SignedBatchTransaction { /** * The fully-signed transaction, base64-encoded. Broadcast it as-is later via * `rpc.sendTransaction(blob, { encoding: 'base64' })`. */ blob: string; /** The transaction signature (also the fee payer's signature). */ signature: Signature; /** * The `lastValidBlockHeight` of the blockhash this transaction was signed * against — the expiry the consumer compares against to know when it is safe to * rebuild (the transaction can no longer land once the chain passes this height). */ lastValidBlockHeight: bigint; /** The instructions that made up this transaction (the packed bucket). */ instructions: Instruction[]; /** The indices of the original `groups` this transaction carried (see {@link BatchTransactionResult.groupIndices}). */ groupIndices: number[]; } /** * Solana service interface * @group @nosana/kit */ export interface SolanaService { readonly config: SolanaConfig; readonly rpc: ReturnType; readonly rpcSubscriptions: ReturnType; readonly sendAndConfirmTransaction: ReturnType; readonly estimateAndSetComputeUnitLimit: ReturnType; airdrop(params: { recipient: Address | string; amount: number | bigint; }): Promise; /** * Optional fee payer for transactions. If set, will be used as fallback when no feePayer is provided in options. * Set this property directly to configure the fee payer. */ feePayer: TransactionSigner | undefined; pda(seeds: Array
, programId: Address): Promise
; /** * Get the SOL balance for a specific address. * * @param addressStr - Optional address to query. If not provided, uses the wallet address. * @returns The SOL balance in lamports * @throws {NosanaError} If neither address nor wallet is provided */ getBalance(addressStr?: string | Address): Promise; /** * Get SOL balance metadata for a specific address. * * @param addressStr - Optional address to query. If not provided, uses the wallet address. * @returns Exact lamports plus display-oriented SOL metadata * @throws {NosanaError} If neither address nor wallet is provided */ getBalanceInfo(addressStr?: string | Address): Promise; /** * Build a transaction message from instructions. * This function creates a transaction message with fee payer, blockhash, and instructions. * * @param instructions Single instruction or array of instructions * @param options Optional configuration * @param options.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. * @param options.estimateComputeUnits If true, estimates and sets the compute unit limit. Default: false. * @returns An unsigned transaction message ready to be signed */ buildTransaction(instructions: Instruction | Instruction[], options?: { feePayer?: TransactionSigner | Address | string; estimateComputeUnits?: boolean; }): Promise; signTransaction(transactionMessage: TransactionMessage & TransactionMessageWithFeePayer & TransactionMessageWithBlockhashLifetime): Promise; sendTransaction(transaction: SendableTransaction & Transaction & TransactionWithBlockhashLifetime, options?: { commitment?: 'processed' | 'confirmed' | 'finalized'; }): Promise; buildSignAndSend(instructions: Instruction | Instruction[], options?: { feePayer?: TransactionSigner; commitment?: 'processed' | 'confirmed' | 'finalized'; estimateComputeUnits?: boolean; }): Promise; /** * Pack instruction groups into the fewest transactions that each stay within the * Solana transaction size limit, then build, sign, and send all of them. * * Each entry of `groups` is an *atomic group*: a single instruction, or an array * of instructions that must stay together in the same transaction. Groups are * never split across transactions; they are greedily packed into buckets sized by * compiling each candidate transaction in-memory (no extra RPC calls). * * Unless `estimateComputeUnits` is set, each transaction gets an explicit * `SetComputeUnitLimit` equal to the sum of its instructions' estimated compute * units (from `computeUnits`), capped at the 1.4M per-transaction maximum. This * keeps priority fees — which are charged against the compute-unit limit — tight * instead of being billed against Solana's inflated per-instruction default. * * All transactions are attempted regardless of individual failures — the returned * array reports the per-transaction outcome in input order. * * @param groups Atomic instruction groups to bulk together. * @param options Optional configuration. * @param options.feePayer Optional fee payer signer. Defaults to the service feePayer or wallet. * @param options.commitment Commitment level for confirmation. * @param options.computeUnits Per-instruction compute-unit estimate: a fixed number for * every instruction, or a function mapping an instruction to its units (return undefined * to fall back to the default). Used to set each transaction's compute-unit limit and to * bound packing. Defaults to Solana's per-instruction default. * @param options.maxComputeUnits Per-transaction compute-unit cap. Defaults to 1,400,000. * @param options.estimateComputeUnits If true, estimates the compute unit limit per transaction * via simulation instead of the static `computeUnits` estimate. Default: false. * @param options.maxTransactionSize Override the maximum serialized transaction size in bytes. * @param options.sequential If true, sends transactions one at a time, confirming each before the * next. Combined with `estimateComputeUnits`, this makes each simulation reflect the chain * state left by the prior transactions (e.g. a market queue that grows or shrinks across the * batch). Default: false (all transactions are sent concurrently). * @returns A per-transaction result array, in the order the buckets were packed. */ buildSignAndSendBatch(groups: Array, options?: { feePayer?: TransactionSigner; commitment?: 'processed' | 'confirmed' | 'finalized'; computeUnits?: number | ((instruction: Instruction) => number | undefined); computeUnitMargin?: number; maxComputeUnits?: number; estimateComputeUnits?: boolean; maxTransactionSize?: number; sequential?: boolean; }): Promise; /** * Pack instruction groups into the fewest transactions, then build and sign each * — but **do not broadcast**. Returns one signed, base64-serialized transaction * per bucket for a separate process to persist and send later. This is * {@link buildSignAndSendBatch} minus the send: it exists so a consumer can * persist-before-send for idempotency (a crash mid-send replays the identical * signed transaction, which the chain dedups by signature). * * Packing and the per-transaction compute-unit limit work exactly as in * {@link buildSignAndSendBatch}. Each bucket is signed with **all** of its * embedded signers (e.g. the fresh job/run keypairs minted by a `list` * instruction) plus the fee payer — this is inherent to signing with the message's * embedded signers, so every required signature is present in the returned blob. * * Performs no network send. Each transaction is signed against its own freshly * fetched blockhash, so `lastValidBlockHeight` may differ per bucket. * * Because the transaction is signed now but may be broadcast later — potentially * against a deeper, costlier state than when it was signed — the static * compute-unit estimate can under-budget at land time. Use `computeUnitMargin` to * over-provision the baked-in limit (the cost of over-budgeting is only fee, and * for size-bound batches it does not reduce packing density). * * @param groups Atomic instruction groups to bulk together. * @param options Same packing options as {@link buildSignAndSendBatch} (no `commitment`/`sequential`, which only apply to sending). * @param options.computeUnitMargin Multiplier on each instruction's compute-unit estimate (default 1). Raise it (>1) to over-provision the limit for transactions broadcast later against a costlier state. * @returns One signed, un-sent transaction per packed bucket, in packing order. */ buildAndSignBatch(groups: Array, options?: { feePayer?: TransactionSigner; computeUnits?: number | ((instruction: Instruction) => number | undefined); computeUnitMargin?: number; maxComputeUnits?: number; estimateComputeUnits?: boolean; maxTransactionSize?: number; }): Promise; /** * 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. * * @param transactionMessage The transaction message to sign (must have fee payer address set and signers embedded in instructions) * @returns A partially signed transaction */ partiallySignTransaction(transactionMessage: TransactionMessage & TransactionMessageWithFeePayer & TransactionMessageWithBlockhashLifetime): Promise; /** * 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). * * @param transaction The transaction to serialize * @returns Base64 encoded wire transaction string */ serializeTransaction(transaction: Transaction): string; /** * 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. * * @param base64 The base64 encoded transaction string * @returns The deserialized transaction with restored lifetime metadata */ deserializeTransaction(base64: string): Promise; /** * 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. * * @param transaction The transaction to sign (typically partially signed, received from another party) * @param signers Array of TransactionPartialSigners to sign with * @returns The signed transaction with additional signatures */ signTransactionWithSigners(transaction: Transaction & TransactionWithBlockhashLifetime, signers: TransactionPartialSigner[]): Promise; /** * 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. * * @param transaction The compiled transaction to decompile * @returns The decompiled transaction message (with either blockhash or durable nonce lifetime) */ decompileTransaction(transaction: Transaction): BaseTransactionMessage & TransactionMessageWithFeePayer & TransactionMessageWithLifetime; /** * Get an instruction to transfer SOL from one address to another. * * @param params Transfer parameters * @param params.to Recipient address * @param params.amount Amount in lamports (number or bigint) * @param params.from Optional sender TransactionSigner. If not provided, uses wallet from client. * @returns An instruction to transfer SOL */ transfer(params: { to: Address | string; amount: number | bigint; from?: TransactionSigner; }): Promise>; /** * Get an instruction to create an associated token account if it doesn't exist. * Checks if the ATA exists, and if not, returns an instruction to create it. * Uses the idempotent version so it's safe to call even if the account already exists. * * @param ata The associated token account address * @param mint The token mint address * @param owner The owner of the associated token account * @param payer Optional payer for the account creation. Can be a TransactionSigner (for full signing) * or an Address (for deferred signing scenarios where the payer signs later). * If not provided, uses the wallet or service feePayer. * @returns An instruction to create the ATA if it doesn't exist, or null if it already exists */ getCreateATAInstructionIfNeeded(ata: Address, mint: Address, owner: Address, payer?: TransactionSigner | Address): Promise> | null>; } /** * Creates a Solana service instance. * @group @nosana/kit */ export declare function createSolanaService(deps: SolanaServiceDeps, config: SolanaConfig): SolanaService; export {}; //# sourceMappingURL=SolanaService.d.ts.map