-
In: gas, deposits, and amounts accept a
string,number, orbigint— plus readable unit strings like"100 Tgas"and"0.01 NEAR". Unit strings are resolved by the local signing path; going through a wallet, convert them yourself withnear.utils.convertUnit. -
Out: what
@fastneardecodes — borshdeserialize,near.ft.*,near.utils.txToJson— gives wide integers back as decimal strings. Results that pass RPC JSON straight through keep NEAR's own types, so yocto fields (deposit,tokens_burnt) are strings whilegas_burntand access-keynonceare JSON numbers. -
Inspect a transaction with
near.explain.tx(...)ornear.utils.txToJson(...)— neverJSON.stringifya rawbigint(it throws). -
You never need
BigIntto build, send, or read 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.
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,
}));
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, but only
the local path resolves unit strings: a wallet forwards gas and
deposit verbatim to its executor, which calls BigInt() on them.
Pass anything readable through near.utils.convertUnit first.
After nearWallet.connect(...), the wallet signs. Load the IIFE globals
(near.js, wallet.js) or import @fastnear/api +
@fastnear/wallet.
// Sign in first: this is what puts the account in near.state, and
// sendTx reads that — not the wallet — to decide who signs.
await near.recipes.connect({ contractId: "berryclub.ek.near" });
// Resolve unit strings BEFORE they cross into a wallet: the wallet
// forwards gas/deposit verbatim to the executor, which calls BigInt().
const result = await near.recipes.functionCall({
receiverId: "berryclub.ek.near",
methodName: "draw",
args: { pixels: [{ x: 10, y: 20, color: 65280 }] },
gas: near.utils.convertUnit("100 Tgas"),
deposit: "0",
});
// Yocto fields (deposit, tokens_burnt) are decimal strings;
// gas_burnt comes straight from the RPC as a JSON number.
near.print(result);
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",
});
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.
await near.recipes.connect({ contractId: "berryclub.ek.near" });
const result = await near.recipes.functionCall({
receiverId: "berryclub.ek.near",
methodName: "buy_tokens",
args: {},
gas: near.utils.convertUnit("100 Tgas"),
// convertUnit also takes a yocto string like "10000000000000000000000"
deposit: near.utils.convertUnit("0.01 NEAR"),
});
near.print(result);
Everything we decode is a string.
Yocto amounts exceed Number's safe range, so @fastnear never
decodes them to plain JS numbers — everything it deserializes comes back as a
decimal string, so a decoded transaction survives
JSON.stringify and re-encodes to identical bytes. Values it merely relays from
the RPC keep whatever type NEAR sent. Reach for BigInt only for arithmetic.
-
JSON.stringify(tx)never throws — there is nobigintto trip on. -
Values from
near.ft.balanceand decoded borsh are strings, so they interoperate without conversion.near.viewis a pass-through — it returns the contract's own JSON, so a field likeft_metadata().decimalsis a number because the contract said so. -
Serializing accepts
string | number | bigint, so a value you read can be sent straight back.
-
@fastnear/borshdeserializereturnsu64/u128as decimal strings by default. -
Pass
deserialize(schema, bytes, { bigints: "bigint" })to opt into nativebigintwhen you want arithmetic. -
u8/u16/u32stay JS numbers (they fit safely).
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.