Constructing a transaction

Build, preview, and send — no BigInt.

A NEAR transaction is a receiverId plus an ordered list of actions. With @fastnear/api you declare the actions, preview them, and sign from a wallet or a local key. There is one rule to learn, and it removes the whole class of big-number snags: you pass amounts in whatever form is handy, and you always get decimal strings back.

The one rule
  • In: gas, deposits, and amounts accept a string, number, or bigint — plus readable unit strings like "100 Tgas" and "0.01 NEAR".
  • Out: wide integers (u64/u128 — amounts, gas, nonces) come back as decimal strings, JSON-safe and matching NEAR RPC.
  • Inspect a transaction with near.explain.tx(...) or near.utils.txToJson(...) — never JSON.stringify a raw bigint (it throws).
  • You never need BigInt to build, send, or read a transaction.
Declare + preview

near.explain.tx is a pure function — it returns a JSON-safe summary of exactly what you are about to sign, with no network call and no wallet popup.

// One action: draw a green pixel on berryclub.ek.near.
const actions = [
  near.actions.functionCall({
    methodName: "draw",
    args: { pixels: [{ x: 10, y: 20, color: 65280 }] }, // color is u32, not a hex string
    gas: "100 Tgas", // or 100000000000000, or 100000000000000n
    deposit: "0",    // draw is not payable — see "Buy tokens" below for a deposit
  }),
];

// Preview before signing — JSON-safe, gas/deposit stay as you wrote them.
near.print(near.explain.tx({
  signerId: "alice.near",
  receiverId: "berryclub.ek.near",
  actions,
}));
Sign and send

The same actions, two signers.

Once the actions are declared, sending is one call. Either a wallet holds the key — the usual browser case — or you sign locally with a full-access key, which is the usual server case but is not limited to Node. Both accept the identical action shape and the same unit strings.

Browser wallet

After nearWallet.connect(...), the wallet signs. Load the IIFE globals (near.js, wallet.js) or import @fastnear/api + @fastnear/wallet.

// Wallet-signed. Units in, no BigInt — the popup confirms the deposit.
const result = await near.recipes.functionCall({
  receiverId: "berryclub.ek.near",
  methodName: "draw",
  args: { pixels: [{ x: 10, y: 20, color: 65280 }] },
  gas: "100 Tgas",
  deposit: "0",
});

near.print(result); // outcome amounts/gas are decimal strings
Node, local key

Sign with a full-access key from @fastnear/utils. Keep the key in server-side secret storage — never ship it to a browser. Passing signer explicitly works on any version; to skip it on every call, persist the key once with near.state.updateAccountState and sendTx will find it (@fastnear/api 2.1.1+ — see keys & accounts).

import { actions, config, sendTx } from "@fastnear/api";
import { signerFromPrivateKey } from "@fastnear/utils";

config({ networkId: "mainnet" });

const result = await sendTx({
  signerId: "alice.near",
  signer: signerFromPrivateKey(privateKey), // full-access key
  receiverId: "berryclub.ek.near",
  actions: [
    actions.functionCall({
      methodName: "draw",
      args: { pixels: [{ x: 10, y: 20, color: 65280 }] },
      gas: "100 Tgas",
      deposit: "0",
    }),
  ],
  waitUntil: "FINAL",
});
Attaching a deposit

draw above is not payable, so its deposit is "0". When a method does take NEAR, write the amount the way you say it — buy_tokens sells 250 🥑 per NEAR, so this buys 2.5.

// Readable units in; the wallet popup confirms the exact deposit.
const result = await near.recipes.functionCall({
  receiverId: "berryclub.ek.near",
  methodName: "buy_tokens",
  args: {},
  gas: "100 Tgas",
  deposit: "0.01 NEAR", // or a yocto string like "10000000000000000000000"
});

near.print(result);
Wide integers

Big numbers are strings, everywhere.

NEAR amounts exceed Number's safe range, so they are never plain JS numbers. Across @fastnear they are decimal strings on the way out — the same shape NEAR JSON-RPC uses — so a decoded transaction survives JSON.stringify and re-encodes to identical bytes. Reach for BigInt only if you need arithmetic.

What this buys you
  • JSON.stringify(tx) never throws — there is no bigint to trip on.
  • Values from near.view, near.ft.balance, and decoded borsh are all strings, so they interoperate without conversion.
  • Serializing accepts string | number | bigint, so a value you read can be sent straight back.
Decoding borsh yourself?
  • @fastnear/borsh deserialize returns u64/u128 as decimal strings by default.
  • Pass deserialize(schema, bytes, { bigints: "bigint" }) to opt into native bigint when you want arithmetic.
  • u8/u16/u32 stay JS numbers (they fit safely).
Go deeper

AI agents: the "Result shapes" section of llms-full.txt states the wide-integers rule, and recipes.json is the structured task catalog (see the explain-transaction, function-call, and transfer recipes). Rate-limited? Free trial credits are at dashboard.fastnear.com — set near.config({ apiKey }) once you have one.