# FastNear JS monorepo (full) Prefer `recipes/index.json` when you need structured task data. ## Packages - `@fastnear/api`: low-level NEAR RPC and FastNear family APIs, plus `near.recipes` task helpers and `near.explain` - `@fastnear/wallet`: wallet connection and transaction/signing provider - `@fastnear/utils`: units, crypto, serialization, storage helpers - `@fastnear/ml-dsa-65`: opt-in protocol-v85 ML-DSA-65 account-key generation, encoding, hashing, and transaction signing - `@fastnear/x402`: official x402 v2 NEAR adapters for paid fetch, local-key clients, resource servers, and facilitators ## Unified config - `near.config({ networkId })` - `near.config({ apiKey })` - `near.config({ nodeUrl })` - `near.config({ retry })` - `near.config({ batch })` ## Resilience and bulk reads `@fastnear/api` retries transient RPC failures (HTTP 408/429/500/502/503/504 and JSON-RPC `-429`/`-32000`) with full-jitter backoff, and exposes an explicit bulk read API. Both are configurable through `near.config` and are on by default. **Retry** — `near.config({ retry })`: - `enabled` (default `true`) — set `false` to restore single-attempt behavior. - `maxAttempts` (`5`) — total attempts including the first. - `baseBackoffMs` (`250`) / `maxBackoffMs` (`30000`) — full-jitter exponential backoff bounds. - `timeoutMs` (`15000`) — per-attempt AbortController timeout (`0` disables it). - `respectRetryAfter` (`true`) — honor a `Retry-After` header, capped at `maxBackoffMs`. - `writePolicy` (`"transport-only"`) — how writes (`send_tx` / `broadcast_tx_*`) retry: `"never"`, `"transport-only"` (only pre-response transport/timeout errors, resending identical signed bytes — safe against double-apply), or `"all"`. **Bulk reads** — concurrency-limited fan-out (NEAR RPC has no array batching, so calls are not merged into one request): - `near.batch(requests)` — each `{ method, params, useArchival?, network? }` runs as its own retried call, at most `batch.maxConcurrency` (default `30`) in flight. Write methods are rejected per-item. - `near.view.many(specs)` — the same fan-out for `{ contractId, methodName, args?, argsBase64?, blockId? }` view specs, decoding each ok result like `near.view`. - `near.config({ batch: { maxConcurrency: 30 } })` tunes the in-flight cap. Both return **settled** results in input order — one failing call never rejects the set: ```js const results = await near.view.many([ { contractId: "token.near", methodName: "ft_balance_of", args: { account_id: "a.near" } }, { contractId: "token.near", methodName: "ft_balance_of", args: { account_id: "b.near" } }, ]); for (const r of results) { if (r.status === "ok") near.print(r.result); else if (r.kind === "contract") console.warn("contract reverted:", r.error); else console.warn("infra error:", r.kind, r.error); } ``` Each error item carries a `kind` — `"contract"` (the contract method reverted or failed), `"transport"` (no HTTP response: network or timeout), `"http"` (non-2xx), or `"rpc"` (JSON-RPC error) — so application failures stay distinguishable from infrastructure ones without re-parsing. Thrown errors are `FastNearRpcError` instances exposing the same `kind`, plus `status`, `code`, `data`, and `retryable`. ## Named endpoint types - `FastNearRecipeDiscoveryEntry` - `AccessKeyListResponse` - `RpcStatusResponse` - `SendTxParams` - `FastNearApiV1AccountFullResponse` - `FastNearApiV1AccountFtResponse` - `FastNearApiV1AccountNftResponse` - `FastNearApiV1AccountStakingResponse` - `FastNearApiV1PublicKeyResponse` - `FastNearApiV1PublicKeyAllResponse` - `FastNearApiV1FtTopResponse` - `FastNearTxTransactionsResponse` - `FastNearTxReceiptResponse` - `FastNearTxAccountResponse` - `FastNearTxBlockResponse` - `FastNearTxBlocksResponse` - `FastNearTransfersQueryResponse` - `FastNearNeardataLastBlockFinalResponse` - `FastNearNeardataLastBlockOptimisticResponse` - `FastNearNeardataBlockResponse` - `FastNearNeardataBlockHeadersResponse` - `FastNearNeardataBlockShardResponse` - `FastNearNeardataBlockChunkResponse` - `FastNearNeardataBlockOptimisticResponse` - `FastNearNeardataFirstBlockResponse` - `FastNearNeardataHealthResponse` - `FastNearKvGetLatestKeyResponse` - `FastNearKvGetHistoryKeyResponse` - `FastNearKvLatestByAccountResponse` - `FastNearKvHistoryByAccountResponse` - `FastNearKvLatestByPredecessorResponse` - `FastNearKvHistoryByPredecessorResponse` - `FastNearKvAllByPredecessorResponse` - `FastNearKvMultiResponse` ## Mental model - Start with `near.view`, `near.queryAccount`, `near.tx.*`, `near.api.v1.*`, `near.transfers.*`, `near.neardata.*`, and `near.fastdata.kv.*` when you want exact control and raw family response shapes. - Use `near.recipes.*` when you want the smallest task helper on top of those lower-level surfaces. - Use `near.recipes.list()` or `near.recipes.toJSON()` when you want to discover the available task helpers at runtime. ## Family chooser ### rpc Canonical NEAR JSON-RPC defaults for direct contract views, account state, and transaction status checks. - Auth style: `query` - Default base URLs: mainnet `https://rpc.mainnet.fastnear.com/`, testnet `https://rpc.testnet.fastnear.com/` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no - Best for: - Direct contract view calls with exact method names and args. - Canonical account state and access key reads. - Low-level RPC questions before you need indexed or aggregated surfaces. - Entrypoints: - `near.view` - `near.queryAccount` - `near.queryAccessKey` - `near.queryAccessKeyList` - `near.queryProtocolVersion` - `near.queryBlock` - `near.queryTx` - `near.sendTx` - `near.ft.balance` - `near.ft.metadata` - `near.ft.totalSupply` - `near.ft.storageBalance` - `near.nft.metadata` - `near.nft.token` - `near.nft.forOwner` - `near.nft.supplyForOwner` - `near.nft.totalSupply` - `near.nft.tokens` ### api FastNear REST aggregations for account holdings, staking, and public-key oriented lookups. - Auth style: `bearer` - Default base URLs: mainnet `https://api.fastnear.com`, testnet `https://test.api.fastnear.com` - Pagination: page_token; request fields: page_token; response fields: page_token; filters must stay stable: yes - Best for: - Combined account snapshots with fungible tokens, NFTs, and staking. - Public-key-to-account discovery. - Questions where one aggregated response is better than stitching multiple RPC calls. - Entrypoints: - `near.api.v1.accountFull` - `near.api.v1.accountFt` - `near.api.v1.accountNft` - `near.api.v1.accountStaking` - `near.api.v1.publicKey` - `near.api.v1.publicKeyAll` - `near.api.v1.ftTop` - `near.ft.inventory` - `near.nft.inventory` ### tx Indexed transaction and receipt lookups for readable execution history by hash, account, or block. - Auth style: `bearer` - Default base URLs: mainnet `https://tx.main.fastnear.com`, testnet `https://tx.test.fastnear.com` - Pagination: resume_token; request fields: resume_token; response fields: resume_token; filters must stay stable: yes - Best for: - Starting from one transaction hash or receipt id. - Readable execution stories with receipts already joined in. - Recent account or block-centered transaction history queries. - Entrypoints: - `near.tx.transactions` - `near.tx.receipt` - `near.tx.account` - `near.tx.block` - `near.tx.blocks` ### transfers Asset-movement-focused history for accounts when the question is specifically about transfers, not full execution. - Auth style: `bearer` - Default base URLs: mainnet `https://transfers.main.fastnear.com`, testnet `null` - Pagination: resume_token; request fields: resume_token; response fields: resume_token; filters must stay stable: yes - Best for: - Recent transfer feeds for one account. - Asset movement summaries across FT, NFT, and native transfers. - Survey scripting where transfer rows matter more than transaction internals. - Entrypoints: - `near.transfers.query` ### neardata Block and shard documents for recent chain-state inspection without reconstructing shard layouts yourself. - Auth style: `query` - Default base URLs: mainnet `https://mainnet.neardata.xyz`, testnet `https://testnet.neardata.xyz` - Pagination: range; request fields: blockHeight, from_block_height, to_block_height; response fields: none; filters must stay stable: no - Best for: - Recent block inspection and shard-aware exploration. - Questions about network recency or recent transaction volume. - Walking block-height ranges and chunk layouts. - Entrypoints: - `near.neardata.lastBlockFinal` - `near.neardata.lastBlockOptimistic` - `near.neardata.block` - `near.neardata.blockHeaders` - `near.neardata.blockShard` - `near.neardata.blockChunk` - `near.neardata.blockOptimistic` - `near.neardata.firstBlock` - `near.neardata.health` ### fastdata.kv Indexed key-value history for exact keys, predecessor scans, and account-scoped storage exploration. - Auth style: `bearer` - Default base URLs: mainnet `https://kv.main.fastnear.com`, testnet `https://kv.test.fastnear.com` - Pagination: resume_token; request fields: resume_token; response fields: resume_token; filters must stay stable: yes - Best for: - Exact-key lookups when you already know the contract, predecessor, and key. - Storage history scans keyed by predecessor or current account. - Questions about SocialDB-style writes and indexed storage history. - Entrypoints: - `near.fastdata.kv.getLatestKey` - `near.fastdata.kv.getHistoryKey` - `near.fastdata.kv.latestByAccount` - `near.fastdata.kv.historyByAccount` - `near.fastdata.kv.latestByPredecessor` - `near.fastdata.kv.historyByPredecessor` - `near.fastdata.kv.allByPredecessor` - `near.fastdata.kv.multi` ## Result shapes - `near.query*` (`queryAccount`, `queryBlock`, `queryAccessKey`, `queryTx`) are JSON-RPC passthroughs and return the raw envelope `{ jsonrpc, result, id }` — read your data from the `.result` field. - `near.view`, `near.recipes.*`, `near.ft.*`, `near.nft.*` and the indexed REST families (`near.tx.*`, `near.api.v1.*`, `near.transfers.*`, `near.neardata.*`, `near.fastdata.kv.*`) return the flat data shape directly. The recipes layer in particular is what flattens `near.queryAccount` results into `{ amount, block_height, storage_usage, ... }` for `near.recipes.viewAccount`. - **Wide integers are strings.** Amounts, gas, deposits, nonces and other `u64`/`u128` values come back as **decimal strings** (JSON-safe, matching NEAR JSON-RPC). Constructing a transaction accepts `string | number | bigint` and unit strings like `"100 Tgas"` / `"0.01 NEAR"`; inspect a built transaction with `near.utils.txToJson` rather than `JSON.stringify`-ing a raw `bigint`. You never need `BigInt` to build, send, or read a transaction. (`@fastnear/borsh` `deserialize` takes `{ bigints: "bigint" }` to opt back in.) ## Low-level API entrypoints - `near.view` - `near.view.many` (bulk views; settled results) - `near.batch` (bulk RPC; settled results) - `near.queryAccount` - `near.queryAccessKey` - `near.queryAccessKeyList` - `near.queryProtocolVersion` - `near.queryTx` - `near.sendTx` - `near.requestSignIn` - `near.signMessage` - `near.api.v1.accountFull` / `accountFt` / `accountNft` / `accountStaking` / `publicKey` / `publicKeyAll` / `ftTop` - `near.tx.transactions` / `receipt` / `account` / `block` / `blocks` - `near.transfers.query` - `near.neardata.lastBlockFinal` / `lastBlockOptimistic` / `block` / `blockHeaders` / `blockShard` / `blockChunk` / `blockOptimistic` / `firstBlock` / `health` - `near.fastdata.kv.getLatestKey` / `getHistoryKey` / `latestByAccount` / `historyByAccount` / `latestByPredecessor` / `historyByPredecessor` / `allByPredecessor` / `multi` ## Wallet entrypoints (`@fastnear/wallet`) - `nearWallet.connect` / `disconnect` / `restore`: open, close, or rehydrate a session per network. `connect({ network, contractId, manifest })` is the canonical entrypoint; `contractId` mints a function-call key scoped to that contract so zero-deposit calls sign silently. - `nearWallet.sendTransaction({ receiverId, actions, network })` / `sendTransactions`: dispatch one or many transactions through the connected wallet on the chosen network. - `nearWallet.signMessage({ message, recipient, nonce, network })`: NEP-413 message signing. - `nearWallet.signDelegateActions({ delegateActions, signerId?, network? })`: sign NEP-366 delegate actions for gasless relay-based flows. Each request may include `blockHeightTtl`; using it requires a wallet that advertises `signDelegateActionsWithTtl`. The canonical transport result is `{ borshSerializedBase64: string }`, while legacy structured and bare-base64 results remain accepted during the bridge transition. - `nearWallet.addFunctionCallKey({ contractId, methodNames, allowance, network })` (`@fastnear/wallet@1.1.4+`): grant a second function-call key on another contract after sign-in, so a follow-on zero-deposit call to that contract also signs silently. - `nearWallet.accountId` / `isConnected` / `connectedNetworks` / `switchNetwork`: per-network session inspection and the active-network cursor. - `nearWallet.onConnect` / `onDisconnect`: subscribe to session lifecycle. ## Access and chaining - API key env var: `FASTNEAR_API_KEY` - Hosted recipe catalog: `https://js.fastnear.com/recipes.json` - Hosted terminal wrapper: `https://js.fastnear.com/agents.js` - Hosted topic explainers: `https://js.fastnear.com/transactions.html` (Constructing a transaction), `https://js.fastnear.com/x402.html` (x402 payments on NEAR), `https://js.fastnear.com/post-quantum.html` (Post-quantum ML-DSA-65 keys), `https://js.fastnear.com/retries.html` (Retries and bulk reads), `https://js.fastnear.com/intents.html` (NEAR Intents) - Free trial credits: `https://dashboard.fastnear.com` Set `FASTNEAR_API_KEY` before running the authenticated snippets. ### Discovery order 1. Read llms.txt — Start with the concise repo and runtime map. 2. Fetch recipes.json — Use the hosted machine-readable recipe catalog with stable IDs, families, auth, returns, and snippets. 3. Run agents.js — Use the hosted terminal wrapper when you want the FastNear JS surface. 4. Fall back to curl + jq — Use raw transport when survey scripting or HTTP-level inspection is more useful. ### Capture and chain one result Keep the object work in JS, then hand the emitted JSON back to shell tooling when you need one more filter step. Every `near.recipes.*`, `near.view`, `near.ft.*`, and `near.nft.*` accepts a per-call `{ network: "testnet" }` override; see the `connect-testnet` and `function-call-testnet` recipes for the end-to-end testnet flow. ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. ACCOUNT_SUMMARY="$(node -e "$(curl -fsSL https://js.fastnear.com/agents.js)" <<'EOF' const account = await near.recipes.viewAccount("root.near"); const { block_hash, storage_usage } = account; near.print({ block_hash, storage_usage }); EOF )" BLOCK_HASH="$(printf '%s\n' "$ACCOUNT_SUMMARY" | jq -r '.block_hash')" STORAGE_USAGE="$(printf '%s\n' "$ACCOUNT_SUMMARY" | jq -r '.storage_usage')" printf 'block_hash=%s\nstorage_usage=%s\n' "$BLOCK_HASH" "$STORAGE_USAGE" ``` ## x402 payments on NEAR `@fastnear/x402` adapts the official x402 Foundation NEAR mechanism without introducing another wire format. - Protocol: x402 v2 `exact` on `near:mainnet` and `near:testnet`. - Authorization: NEP-366 SignedDelegate; asset: NEP-141 fungible tokens. - Browser global: `nearX402`. - Runtime: Package-only; not included in agents.js or near.js. - Browser status: Stable with tested Meteor Wallet support; other wallets must advertise both timeout-aware delegate-signing capabilities and pass the x402 testnet harness before being documented as compatible. - Required wallet features: `signDelegateActions` and `signDelegateActionsWithTtl`. - Package guide: [https://github.com/fastnear/js-monorepo/blob/main/packages/x402/README.md](https://github.com/fastnear/js-monorepo/blob/main/packages/x402/README.md). ### Choose by task - Pay an x402 URL from Node.js: `createLocalNearSigner` + `createNearPaymentFetch` from `@fastnear/x402/node` and `@fastnear/x402` — stable. - Pay an x402 URL from a browser wallet: `createFastNearWalletSigner` + `createNearPaymentFetch` from `@fastnear/wallet` and `@fastnear/x402` — stable with a compatible timeout-aware wallet; Meteor Wallet is the tested production path. - Protect a seller resource: `createNearResourceServer` from `@fastnear/x402/server` — requires an explicit facilitator. - Operate a NEAR facilitator: `createNearFacilitator` from `@fastnear/x402/facilitator` — HTTP framework and secret storage are operator choices. - Integrate below the paid-fetch helper: `createNearX402Client` from `@fastnear/x402` — lower-level client path. ### Entrypoints - `@fastnear/x402`: `createFastNearWalletSigner`, `createNearX402Client`, `createNearPaymentFetch` — injected FastNEAR wallet signer, NEAR-only x402 client, and paid fetch. - `@fastnear/x402/node`: `createLocalNearSigner` — official RPC-backed local full-access-key signer. - `@fastnear/x402/server`: `createNearResourceServer` — resource server with one or more explicitly configured facilitators. - `@fastnear/x402/facilitator`: `createNearFacilitator` — self-hosted facilitator registration for concrete NEAR networks. ### Constraints - Only x402 v2 exact payments on near:mainnet and near:testnet are supported. - Payments use NEP-141 tokens; native NEAR is not a direct payment asset. - Wallet and local-key payers require full-access keys, and recipients need token storage registration. - Resource servers require an explicit facilitator; no x402.org or other default is selected. - Browser wallet access is injected explicitly and payment occurs only when the application calls the paid fetch function. ### Safe defaults - Pin near:testnet during development and a concrete NEAR network in production; use near:* only for an intentionally cross-network client. - Keep payer and relayer secret keys in server-side secret storage, never browser code. - String and number seller prices use the official USDC contract; wNEAR and custom tokens require an explicit { amount, asset } price. - Always configure a facilitator explicitly. ### Pay an x402 URL from Node.js Quickstart ID: `x402-node-paid-fetch` Use the upstream local full-access-key signer with the high-level paid-fetch helper. ```js import { createNearPaymentFetch } from "@fastnear/x402"; import { createLocalNearSigner } from "@fastnear/x402/node"; const { NEAR_PAYER_ACCOUNT_ID, NEAR_PAYER_SECRET_KEY, X402_RESOURCE_URL } = process.env; if (!NEAR_PAYER_ACCOUNT_ID || !NEAR_PAYER_SECRET_KEY || !X402_RESOURCE_URL) { throw new Error("NEAR_PAYER_ACCOUNT_ID, NEAR_PAYER_SECRET_KEY, and X402_RESOURCE_URL are required"); } const signer = createLocalNearSigner({ accountId: NEAR_PAYER_ACCOUNT_ID, secretKey: NEAR_PAYER_SECRET_KEY, rpcUrls: { "near:testnet": "https://rpc.testnet.fastnear.com" }, }); const paidFetch = createNearPaymentFetch({ signer, network: "near:testnet" }); const response = await paidFetch(X402_RESOURCE_URL); if (!response.ok) throw new Error(`Paid request failed: ${response.status}`); console.log(await response.json()); ``` ### Configure a seller with an explicit remote facilitator Quickstart ID: `x402-remote-facilitator-seller` Create the NEAR resource-server core, then pass it to the x402 HTTP framework adapter you choose. ```js import { createNearResourceServer } from "@fastnear/x402/server"; const { X402_FACILITATOR_URL } = process.env; if (!X402_FACILITATOR_URL) throw new Error("X402_FACILITATOR_URL is required"); export const resourceServer = createNearResourceServer({ facilitators: { url: X402_FACILITATOR_URL }, }); await resourceServer.initialize(); ``` ## ML-DSA-65 account-key quickstarts The opt-in `@fastnear/ml-dsa-65` package provides protocol-v85 account access keys and transaction signatures without pulling the post-quantum backend into `@fastnear/api` or `@fastnear/utils`. - Runtime: Node.js 20.19+ or a modern browser. - Scope: NEAR account access keys and transaction signatures only; validator and staking keys remain Ed25519. - Exact byte lengths: seed 32, public key 1952, expanded secret key 4032, signature 3309. - Verification charge: 100 Ggas (100000000000 gas) for each outer or delegated ML-DSA-65 signature verification. - Full key: `ml-dsa-65:`; list handle: `ml-dsa-65-hash:`. - Handle derivation: SHA3-256 of the ASCII domain tag followed by the raw 1,952-byte public key; domain tag `near:ml-dsa-65-pubkey-hash:v1`. Use the full public key for AddKey, direct access-key lookup, signing, and DeleteKey. Access-key list responses expose the compact handle; derive it with publicKeyToHandle() before comparing. ### Safety constraints - Check the selected RPC's active protocol_version and require 85 or later before adding or using an ML-DSA-65 key; do not use node software versions or latest_protocol_version as activation signals. - Never print or persist generated seeds or expanded secret keys. Keep a temporary recovery record public-only: network, account ID, full public key, and hash handle. - After an AddKey attempt, do not trust a single absence read: submit a finalized classical DeleteKey nonce barrier, confirm absence at finality, and only then remove the public recovery record. - ML-DSA-65 public keys are 1,952 bytes and signatures are 3,309 bytes, so transactions and key-management actions are substantially larger than classical equivalents. - NEAR charges 100 Ggas (100,000,000,000 gas) for each outer or delegated ML-DSA-65 signature verification. - The selected @noble/post-quantum backend describes itself as self-audited and does not claim constant-time side-channel protection. Prefer a native, WASM, HSM, or hardware TransactionSigner when that threat model requires one. - destroy() provides best-effort zeroization of package-owned JavaScript buffers, not a hard memory-erasure guarantee. Constrained QuickJS and MCU runtimes are not a v1 compatibility target. ### Generate an in-memory ML-DSA-65 signer Recipe ID: `ml-dsa-65-generate` Generate the opt-in signer, retain only public recovery metadata, and always destroy the signer when its lifecycle ends. ```js import { generateSigner } from "@fastnear/ml-dsa-65"; const signer = generateSigner(); try { // Public values are safe to retain for enrollment and cleanup. const recovery = { network: "testnet", accountId: "device.testnet", publicKey: signer.publicKey, publicKeyHandle: signer.publicKeyHandle, }; console.log(recovery); // Never log or persist signer.exportSeed() or signer.exportSecretKey(). } finally { signer.destroy(); } ``` ### Send with an enrolled ML-DSA-65 signer Recipe ID: `ml-dsa-65-explicit-send` Use the explicit-signer branch of sendTx after the signer's full public key has been enrolled on the account. ```js import { actions, queryProtocolVersion, sendTx, } from "@fastnear/api"; export async function sendOneYoctoWithMlDsa65({ accountId, signer }) { const protocolVersion = await queryProtocolVersion({ network: "testnet" }); if (protocolVersion < 85) { throw new Error(`testnet protocol ${protocolVersion} does not support ML-DSA-65`); } return sendTx({ signerId: accountId, signer, receiverId: accountId, actions: [actions.transfer("1")], waitUntil: "FINAL", network: "testnet", }); } ``` ### Enroll and delete a temporary testnet key Recipe ID: `ml-dsa-65-enroll-delete` Persist public-only recovery metadata, use an authorized classical full-access signer for both mutations, and establish finalized deletion before removing the record. ```js import { actions, queryAccessKeyList, queryProtocolVersion, sendTx, } from "@fastnear/api"; import { generateSigner, } from "@fastnear/ml-dsa-65"; export async function withTemporaryMlDsa65Key({ accountId, classicalSigner, run, saveRecovery, removeRecovery, }) { if (!accountId.endsWith(".testnet")) { throw new Error("This safety-oriented recipe is testnet-only"); } const protocolVersion = await queryProtocolVersion({ network: "testnet" }); if (protocolVersion < 85) { throw new Error(`testnet protocol ${protocolVersion} does not support ML-DSA-65`); } const signer = generateSigner(); const publicRecovery = { network: "testnet", accountId, publicKey: signer.publicKey, publicKeyHandle: signer.publicKeyHandle, }; let addAttempted = false; async function deleteWithFinalizedBarrier() { let lastError; for (let attempt = 1; attempt <= 3; attempt += 1) { try { // Submit even when one read says the key is absent. A finalized // classical transaction prevents an ambiguous earlier AddKey from // landing later with the same or a lower nonce. await sendTx({ signerId: accountId, signer: classicalSigner, receiverId: accountId, actions: [actions.deleteKey({ publicKey: signer.publicKey })], waitUntil: "FINAL", network: "testnet", }); const list = await queryAccessKeyList({ accountId, blockId: "final", network: "testnet", }); const stillPresent = list.result.keys.some( (entry) => entry.public_key === publicRecovery.publicKeyHandle, ); if (!stillPresent) return; lastError = new Error("ML-DSA-65 key remains after finalized deletion"); } catch (error) { lastError = error; } } throw lastError ?? new Error("Could not establish ML-DSA-65 key absence"); } try { // Implement these callbacks with durable application storage. On Node, // create the public-only file with mode 0600. Never include secret bytes. await saveRecovery(publicRecovery); addAttempted = true; await sendTx({ signerId: accountId, signer: classicalSigner, receiverId: accountId, actions: [actions.addFullAccessKey({ publicKey: signer.publicKey })], waitUntil: "FINAL", network: "testnet", }); return await run(signer); } finally { try { if (addAttempted) { await deleteWithFinalizedBarrier(); await removeRecovery(publicRecovery); } } finally { signer.destroy(); } } } ``` ### Reconcile a full key with its access-key-list handle Recipe ID: `ml-dsa-65-reconcile` Query the full key directly, then match its locally derived hash handle against the compact list response. ```js import { queryAccessKey, queryAccessKeyList, } from "@fastnear/api"; import { publicKeyToHandle } from "@fastnear/ml-dsa-65"; export async function findMlDsa65AccessKey({ accountId, publicKey }) { const [direct, list] = await Promise.all([ queryAccessKey({ accountId, publicKey, network: "testnet" }), queryAccessKeyList({ accountId, network: "testnet" }), ]); const publicKeyHandle = publicKeyToHandle(publicKey); const listed = list.result.keys.find( (entry) => entry.public_key === publicKeyHandle, ); return { direct: direct.result, publicKeyHandle, listed }; } ``` ## NEAR Intents `@fastnear/intents` integrates the NEAR Intents protocol: the intents.near verifier, the hosted 1Click swap API, and the solver relay. - Verifier: `intents.near` (mainnet); ledger: NEP-245 multi-token; token ids nep141:, nep171::, nep245::. - Signing: NEP-413 signed messages (full-access keys only); the verifier also accepts erc191, tip191, raw_ed25519, webauthn, ton_connect, and sep53 payloads. - 1Click API: `https://1click.chaindefuser.com`; solver relay: `https://solver-relay-v2.chaindefuser.com/rpc`. - Browser global: `nearIntents`. - Runtime: Package-only; not included in agents.js or near.js. - Wallet status: The wallet signing path uses nearWallet.signMessage (NEP-413), which every near-connect executor implements; the funded end-to-end swap path is verified by the mainnet smoke runbook before being documented further. - Package guide: [https://github.com/fastnear/js-monorepo/blob/main/packages/intents/README.md](https://github.com/fastnear/js-monorepo/blob/main/packages/intents/README.md). ### Choose by task - Quote and track a swap: `createOneClickClient` from `@fastnear/intents` — stable; keyless use adds a 0.2% platform fee to quotes. - Sign intents from a browser wallet: `createWalletIntentSigner` from `@fastnear/wallet` and `@fastnear/intents` — NEP-413 via the connected wallet's full-access key; FunctionCall session keys cannot sign intents. - Sign intents from Node.js or an agent: `createLocalIntentSigner` from `@fastnear/intents/node` — raw full-access key, server-side only. - Deposit, check balances, withdraw on the verifier: `ftDepositAction` + `wrapNearAction` + `mtBatchBalances` + `ftWithdrawAction` from `@fastnear/intents` and `@fastnear/api` — action builders for near.sendTx plus NEP-245 views over injected near.view. - Talk to the solver relay directly: `createSolverRelayClient` from `@fastnear/intents/relay` — quotes require a partner API key in practice; the 1Click path is the default. ### Entrypoints - `@fastnear/intents`: `createOneClickClient`, `createWalletIntentSigner`, `createSolverRelayClient`, `ftDepositAction`, `wrapNearAction`, `ftWithdrawAction`, `mtBalance`, `mtBatchBalances`, `toSignedIntent`, `randomNonce` — browser-safe 1Click client, wallet intent signer, verifier helpers, and relay client. - `@fastnear/intents/relay`: `createSolverRelayClient` — solver-relay JSON-RPC client (quote, publish_intent, publish_intents, get_status). - `@fastnear/intents/node`: `createLocalIntentSigner` — local full-access-key NEP-413 intent signer for servers and agents. ### Constraints - The verifier is intents.near on NEAR mainnet; there is no public testnet deployment of the intents stack. - NEP-413 intent signatures require a full-access key, and the verifier checks the key is authorized for signer_id. - Submitted signatures use ed25519: encoding — not the base64 NEAR wallets return; the signers own that conversion. - Native NEAR is not a verifier asset: wrap to wNEAR before depositing, and exit native NEAR only via the native_withdraw intent. - Amounts are base-unit strings, and token_diff diffs must net to zero per token across the executed batch. ### Safe defaults - Quote with dry:true first; commit with dry:false only when ready to fund the deposit address before it expires. - Keep local signer private keys in server-side secret storage, never browser code. - Use a partner API key from partners.near-intents.org to remove the 0.2% keyless platform fee. - Poll /v0/status to a terminal state (SUCCESS, REFUNDED, FAILED) and surface swapDetails on non-success. - Omit msg on ft_withdraw so failed withdrawals stay refundable. ### Quote a swap and read live pricing Quickstart ID: `intents-one-click-quote` Discover assets and price a swap with a free dry-run quote — no auth, no funds, no commitment. ```js import { createOneClickClient } from "@fastnear/intents"; const oneClick = createOneClickClient(); const tokens = await oneClick.tokens(); const quote = await oneClick.quote({ dry: true, swapType: "EXACT_INPUT", slippageTolerance: 100, originAsset: "nep141:wrap.near", destinationAsset: "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1", amount: "1000000000000000000000000", depositType: "ORIGIN_CHAIN", refundTo: "you.near", refundType: "ORIGIN_CHAIN", recipient: "you.near", recipientType: "DESTINATION_CHAIN", deadline: new Date(Date.now() + 10 * 60_000).toISOString(), }); console.log(quote.quote.amountOutFormatted); ``` ### Sign a token_diff intent with a browser wallet Quickstart ID: `intents-wallet-sign` NEP-413 through the connected wallet, re-encoded to the MultiPayload the verifier accepts. ```js import { createWalletIntentSigner } from "@fastnear/intents"; // window.nearWallet from https://js.fastnear.com/wallet.js, already connected. const signer = createWalletIntentSigner({ wallet: nearWallet }); const signed = await signer.signIntents({ intents: [{ intent: "token_diff", diff: { "nep141:usdc.near": "-1000000", "nep141:usdt.near": "1000000", }, }], }); // signed = { standard: "nep413", payload, public_key, signature } — submit via // oneClick.submitIntent, relay.publishIntent, or intents.near execute_intents. ``` ### Swap intents.near balances from Node.js Quickstart ID: `intents-node-swap` The INTENTS deposit type: 1Click builds the payload, the local signer signs it verbatim, no deposit transaction needed. ```js import { createOneClickClient } from "@fastnear/intents"; import { createLocalIntentSigner } from "@fastnear/intents/node"; const { NEAR_ACCOUNT_ID, NEAR_PRIVATE_KEY } = process.env; const oneClick = createOneClickClient(); const signer = createLocalIntentSigner({ accountId: NEAR_ACCOUNT_ID, privateKey: NEAR_PRIVATE_KEY, // full-access, server-side only }); // Quote with depositType/refundType/recipientType "INTENTS" — the input // funds already sit inside intents.near, so no deposit transaction is needed. const quote = await oneClick.quote({ dry: false, swapType: "EXACT_INPUT", slippageTolerance: 100, originAsset: "nep141:wrap.near", destinationAsset: "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1", amount: "1000000000000000000000000", depositType: "INTENTS", refundTo: NEAR_ACCOUNT_ID, refundType: "INTENTS", recipient: NEAR_ACCOUNT_ID, recipientType: "INTENTS", deadline: new Date(Date.now() + 10 * 60_000).toISOString(), }); const { intent } = await oneClick.generateIntent({ signerId: NEAR_ACCOUNT_ID, depositAddress: quote.quote.depositAddress, }); // signPayload pins the recipient to intents.near and signs verbatim. const signed = await signer.signPayload(intent); const { intentHash } = await oneClick.submitIntent({ signedData: signed }); console.log(intentHash); ``` ### Deposit wNEAR and read verifier balances Quickstart ID: `intents-deposit-balances` Action builders for near.sendTx plus NEP-245 ledger views over the injected near.view. ```js import { ftDepositAction, wrapNearAction, mtBatchBalances } from "@fastnear/intents"; await near.sendTx({ receiverId: "wrap.near", actions: [wrapNearAction({ amountYocto: "1000000000000000000000000" })], }); await near.sendTx({ receiverId: "wrap.near", actions: [ftDepositAction({ amount: "1000000000000000000000000" })], }); const balances = await mtBatchBalances({ accountId: near.accountId(), tokenIds: ["nep141:wrap.near"], view: near.view, }); near.print(balances); ``` ## Recipe catalog ## What does this contract method return? - ID: `view-contract` - API: `near.recipes.viewContract` - Service: `rpc` - Returns: `BerryClubAccountView` - Network: `mainnet` - Auth: `none` - Summary: Start with one view call when you already know the contract, method, and arguments. - Output keys: `account_id`, `avocado_balance`, `num_pixels` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when you already know the exact contract method and want the smallest answer quickly. - Stay on RPC when the question is still about one direct view call rather than indexed history. Response notes: - This recipe returns the parsed JSON value from the contract method, not the raw RPC wrapper. - Use the curl + jq variant when you want to inspect the encoded args or the raw RPC envelope. Follow-ups: - If the method output looks wrong, compare it with the canonical account state from near.recipes.viewAccount. - If you need a broader ownership snapshot, move to near.api.v1.accountFull. Related recipes: - `view-account` - `account-full` Example inputs: ```json { "contractId": "berryclub.ek.near", "methodName": "get_account", "args": { "account_id": "root.near" } } ``` ### Terminal ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. node -e "$(curl -fsSL https://js.fastnear.com/agents.js)" <<'EOF' const result = await near.recipes.viewContract({ contractId: "berryclub.ek.near", methodName: "get_account", args: { account_id: "root.near" }, }); near.print({ account_id: result.account_id, avocado_balance: result.avocado_balance, num_pixels: result.num_pixels, }); EOF ``` ### curl + jq ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. ACCOUNT_ID=root.near ARGS_BASE64="$(jq -nc --arg account_id "$ACCOUNT_ID" '{account_id: $account_id}' | base64 | tr -d '\n')" curl -sS "https://rpc.mainnet.fastnear.com?apiKey=$FASTNEAR_API_KEY" -H 'content-type: application/json' --data "$(jq -nc --arg args "$ARGS_BASE64" '{ jsonrpc:"2.0",id:"fastnear",method:"query", params:{ request_type:"call_function", finality:"final", account_id:"berryclub.ek.near", method_name:"get_account", args_base64:$args } }')" | jq '.result.result | implode | fromjson | {account_id, avocado_balance, num_pixels}' ``` ### Browser Global ```js const result = await near.recipes.viewContract({ contractId: "berryclub.ek.near", methodName: "get_account", args: { account_id: "root.near" }, }); near.print({ account_id: result.account_id, avocado_balance: result.avocado_balance, num_pixels: result.num_pixels, }); ``` ### ESM ```js import * as near from "@fastnear/api"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); const result = await near.recipes.viewContract({ contractId: "berryclub.ek.near", methodName: "get_account", args: { account_id: "root.near" }, }); near.print({ account_id: result.account_id, avocado_balance: result.avocado_balance, num_pixels: result.num_pixels, }); ``` ## What does this account look like on chain? - ID: `view-account` - API: `near.recipes.viewAccount` - Service: `rpc` - Returns: `RpcViewAccountResponse` - Network: `mainnet` - Auth: `none` - Summary: Use canonical RPC account state when the question is still about one account record. - Output keys: `amount`, `locked`, `storage_usage`, `block_height`, `block_hash` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when the question is about one account's chain state rather than token holdings or transfers. - Use this before reaching for aggregations if you need the raw account state answer first. Response notes: - This is the canonical on-chain account record from JSON-RPC. - The snippet keeps the object work in JS and emits only the fields most useful for survey scripting. Follow-ups: - If the account is active but you need holdings, switch to near.api.v1.accountFull. - If you need recent transfer activity, continue with near.transfers.query. Related recipes: - `account-full` - `transfers-query` Example inputs: ```json { "accountId": "root.near" } ``` ### Terminal ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. node -e "$(curl -fsSL https://js.fastnear.com/agents.js)" <<'EOF' const account = await near.recipes.viewAccount("root.near"); const { amount, locked, storage_usage, block_height, block_hash } = account; near.print({ amount, locked, storage_usage, block_height, block_hash, }); EOF ``` ### curl + jq ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. ACCOUNT_ID=root.near curl -sS "https://rpc.mainnet.fastnear.com?apiKey=$FASTNEAR_API_KEY" -H 'content-type: application/json' --data "$(jq -nc --arg account_id "$ACCOUNT_ID" '{ jsonrpc:"2.0",id:"fastnear",method:"query", params:{request_type:"view_account",account_id:$account_id,finality:"final"} }')" | jq '.result | {amount, locked, storage_usage, block_height, block_hash}' ``` ### Browser Global ```js const account = await near.recipes.viewAccount("root.near"); const { amount, locked, storage_usage, block_height, block_hash } = account; near.print({ amount, locked, storage_usage, block_height, block_hash, }); ``` ### ESM ```js import * as near from "@fastnear/api"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); const account = await near.recipes.viewAccount("root.near"); const { amount, locked, storage_usage, block_height, block_hash } = account; near.print({ amount, locked, storage_usage, block_height, block_hash, }); ``` ## What happened in this transaction? - ID: `inspect-transaction` - API: `near.tx.transactions` - Service: `tx` - Returns: `FastNearTxTransactionsResponse` - Network: `mainnet` - Auth: `none` - Summary: Start with the indexed transaction family when all you have is the hash and you want the readable story. - Output keys: `transactions[].transaction.hash`, `transactions[].transaction.signer_id`, `transactions[].transaction.receiver_id`, `transactions[].execution_outcome.block_height`, `transactions[].receipts` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when the only durable identifier you have is the transaction hash. - Prefer this over transfers when you need the execution story, receipts, or included block details. Response notes: - The low-level tx family returns raw indexed JSON with receipts already attached. - This recipe narrows that response to the one transaction row and prints a compact human-readable summary. Follow-ups: - If you care only about asset movement, pivot to near.transfers.query. - If you need a block-centered history scan, continue with near.tx.account or near.tx.blocks. Related recipes: - `transfers-query` - `last-block-final` Example inputs: ```json { "txHashes": [ "7ZKnhzt2MqMNmsk13dV8GAjGu3Db8aHzSBHeNeu9MJCq" ] } ``` ### Terminal ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. node -e "$(curl -fsSL https://js.fastnear.com/agents.js)" <<'EOF' const tx = await near.recipes.inspectTransaction( "7ZKnhzt2MqMNmsk13dV8GAjGu3Db8aHzSBHeNeu9MJCq" ); near.print( tx ? { hash: tx.transaction.hash, signer_id: tx.transaction.signer_id, receiver_id: tx.transaction.receiver_id, included_block_height: tx.execution_outcome.block_height, receipt_count: tx.receipts.length, } : null ); EOF ``` ### curl + jq ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. TX_HASH=7ZKnhzt2MqMNmsk13dV8GAjGu3Db8aHzSBHeNeu9MJCq curl -sS "https://tx.main.fastnear.com/v0/transactions" -H "Authorization: Bearer $FASTNEAR_API_KEY" -H 'content-type: application/json' --data "$(jq -nc --arg tx_hash "$TX_HASH" '{tx_hashes: [$tx_hash]}')" | jq '{ hash: .transactions[0].transaction.hash, signer_id: .transactions[0].transaction.signer_id, receiver_id: .transactions[0].transaction.receiver_id, included_block_height: .transactions[0].execution_outcome.block_height, receipt_count: (.transactions[0].receipts | length) }' ``` ### Browser Global ```js const tx = await near.recipes.inspectTransaction( "7ZKnhzt2MqMNmsk13dV8GAjGu3Db8aHzSBHeNeu9MJCq" ); near.print( tx ? { hash: tx.transaction.hash, signer_id: tx.transaction.signer_id, receiver_id: tx.transaction.receiver_id, included_block_height: tx.execution_outcome.block_height, receipt_count: tx.receipts.length, } : null ); ``` ### ESM ```js import * as near from "@fastnear/api"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); const tx = await near.recipes.inspectTransaction( "7ZKnhzt2MqMNmsk13dV8GAjGu3Db8aHzSBHeNeu9MJCq" ); near.print( tx ? { hash: tx.transaction.hash, signer_id: tx.transaction.signer_id, receiver_id: tx.transaction.receiver_id, included_block_height: tx.execution_outcome.block_height, receipt_count: tx.receipts.length, } : null ); ``` ## What does this account own? - ID: `account-full` - API: `near.api.v1.accountFull` - Service: `api` - Returns: `FastNearApiV1AccountFullResponse` - Network: `mainnet` - Auth: `none` - Summary: Use the FastNear account aggregator when the question is about holdings, NFTs, or staking in one response. - Output keys: `account_id`, `state.balance`, `tokens`, `nfts`, `pools` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when you want holdings, NFTs, and staking without stitching multiple calls together. - Use it after a canonical RPC account check when the next question becomes 'what does this account own?'. Response notes: - This is the aggregated account surface for holdings and staking, not a raw RPC account object. - It is the best one-response answer when the task is portfolio-style discovery. Follow-ups: - If you need movement history instead of holdings, continue with near.transfers.query. - If you need one exact contract state read, go back to near.recipes.viewContract. Related recipes: - `view-account` - `transfers-query` Example inputs: ```json { "accountId": "root.near" } ``` ### Terminal ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. node -e "$(curl -fsSL https://js.fastnear.com/agents.js)" <<'EOF' const account = await near.api.v1.accountFull({ accountId: "root.near", }); near.print({ account_id: account.account_id, near_balance_yocto: account.state.balance, ft_contracts: account.tokens.length, nft_contracts: account.nfts.length, staking_pool_contracts: account.pools.length, }); EOF ``` ### curl + jq ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. ACCOUNT_ID=root.near curl -sS "https://api.fastnear.com/v1/account/$ACCOUNT_ID/full" -H "Authorization: Bearer $FASTNEAR_API_KEY" | jq '{ account_id, near_balance_yocto: .state.balance, ft_contracts: (.tokens | length), nft_contracts: (.nfts | length), staking_pool_contracts: (.pools | length) }' ``` ### Browser Global ```js const account = await near.api.v1.accountFull({ accountId: "root.near", }); near.print({ account_id: account.account_id, near_balance_yocto: account.state.balance, ft_contracts: account.tokens.length, nft_contracts: account.nfts.length, staking_pool_contracts: account.pools.length, }); ``` ### ESM ```js import * as near from "@fastnear/api"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); const account = await near.api.v1.accountFull({ accountId: "root.near", }); near.print({ account_id: account.account_id, near_balance_yocto: account.state.balance, ft_contracts: account.tokens.length, nft_contracts: account.nfts.length, staking_pool_contracts: account.pools.length, }); ``` ## What is this account's recent transfer activity? - ID: `transfers-query` - API: `near.transfers.query` - Service: `transfers` - Returns: `FastNearTransfersQueryResponse` - Network: `mainnet` - Auth: `none` - Summary: Use the transfers family when the question is specifically about asset movement, not the broader execution story. - Output keys: `transfers[].block_height`, `transfers[].asset_id`, `transfers[].human_amount`, `transfers[].other_account_id`, `transfers[].transaction_id`, `resume_token` - Pagination: resume_token; request fields: resume_token; response fields: resume_token; filters must stay stable: yes Choose this when: - Choose this when the question is about who sent what asset and when. - Prefer this over near.tx when you do not need receipt details or execution outcomes. Response notes: - Transfers answers the asset-movement question directly and returns raw rows plus resume-token pagination when available. - It is intentionally narrower than the tx family and better suited to feed-style scripting. Follow-ups: - If one transfer row needs a deeper execution story, pivot to near.tx.transactions with the related hash. - If you need holdings instead of movement, switch to near.api.v1.accountFull. Related recipes: - `inspect-transaction` - `account-full` Example inputs: ```json { "accountId": "root.near", "desc": true, "limit": 5 } ``` ### Terminal ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. node -e "$(curl -fsSL https://js.fastnear.com/agents.js)" <<'EOF' const feed = await near.transfers.query({ accountId: "root.near", desc: true, limit: 5, }); near.print({ recent: (feed.transfers || []).map((entry) => ({ block_height: entry.block_height, asset_id: entry.asset_id, human_amount: entry.human_amount, other_account_id: entry.other_account_id, transfer_type: entry.transfer_type, tx: entry.transaction_id, })), }); EOF ``` ### curl + jq ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. ACCOUNT_ID=root.near curl -sS "https://transfers.main.fastnear.com/v0/transfers" -H "Authorization: Bearer $FASTNEAR_API_KEY" -H 'content-type: application/json' --data "$(jq -nc --arg account_id "$ACCOUNT_ID" '{account_id: $account_id, desc: true, limit: 5}')" | jq '{ recent: [.transfers[] | { block_height, asset_id, human_amount, other_account_id, transfer_type, tx: .transaction_id }] }' ``` ### Browser Global ```js const feed = await near.transfers.query({ accountId: "root.near", desc: true, limit: 5, }); near.print({ recent: (feed.transfers || []).map((entry) => ({ block_height: entry.block_height, asset_id: entry.asset_id, human_amount: entry.human_amount, other_account_id: entry.other_account_id, transfer_type: entry.transfer_type, tx: entry.transaction_id, })), }); ``` ### ESM ```js import * as near from "@fastnear/api"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); const feed = await near.transfers.query({ accountId: "root.near", desc: true, limit: 5, }); near.print({ recent: (feed.transfers || []).map((entry) => ({ block_height: entry.block_height, asset_id: entry.asset_id, human_amount: entry.human_amount, other_account_id: entry.other_account_id, transfer_type: entry.transfer_type, tx: entry.transaction_id, })), }); ``` ## What block is NEAR on right now? - ID: `last-block-final` - API: `near.neardata.lastBlockFinal` - Service: `neardata` - Returns: `FastNearNeardataLastBlockFinalResponse` - Network: `mainnet` - Auth: `none` - Summary: Use the NEAR Data family when you want a recent block document without stitching shards together yourself. - Output keys: `block.header.height`, `block.header.timestamp_nanosec`, `shards[].shard_id`, `shards[].chunk.transactions` - Pagination: range; request fields: blockHeight, from_block_height, to_block_height; response fields: none; filters must stay stable: no Choose this when: - Choose this when the question starts with recent block recency or transaction volume. - Use the family-level block endpoints when you want to walk heights or inspect one shard or chunk next. Response notes: - NEAR Data returns block documents with shard content already grouped for block-level inspection. - This recipe highlights the smallest useful block recency summary while keeping the full response available in JS. Follow-ups: - If one transaction in the block matters, continue with near.tx.transactions. - If you need a specific historical block, move to near.neardata.block or near.neardata.blockChunk. Related recipes: - `inspect-transaction` Example inputs: ```json {} ``` ### Terminal ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. node -e "$(curl -fsSL https://js.fastnear.com/agents.js)" <<'EOF' const block = await near.neardata.lastBlockFinal(); // shard.chunk is null when a shard missed this block — guard before reading. near.print({ height: block.block.header.height, timestamp_nanosec: block.block.header.timestamp_nanosec, txs_per_shard: block.shards.map((shard) => ({ shard_id: shard.shard_id, tx_count: shard.chunk?.transactions.length ?? 0, })), total_txs: block.shards.reduce( (count, shard) => count + (shard.chunk?.transactions.length ?? 0), 0 ), }); EOF ``` ### curl + jq ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. curl -sSL "https://mainnet.neardata.xyz/v0/last_block/final?apiKey=$FASTNEAR_API_KEY" | jq '{ height: .block.header.height, timestamp_nanosec: .block.header.timestamp_nanosec, txs_per_shard: [.shards[] | {shard_id, tx_count: (.chunk.transactions | length)}], total_txs: ([.shards[].chunk.transactions[]?] | length) }' ``` ### Browser Global ```js const block = await near.neardata.lastBlockFinal(); // shard.chunk is null when a shard missed this block — guard before reading. near.print({ height: block.block.header.height, timestamp_nanosec: block.block.header.timestamp_nanosec, txs_per_shard: block.shards.map((shard) => ({ shard_id: shard.shard_id, tx_count: shard.chunk?.transactions.length ?? 0, })), total_txs: block.shards.reduce( (count, shard) => count + (shard.chunk?.transactions.length ?? 0), 0 ), }); ``` ### ESM ```js import * as near from "@fastnear/api"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); const block = await near.neardata.lastBlockFinal(); // shard.chunk is null when a shard missed this block — guard before reading. near.print({ height: block.block.header.height, timestamp_nanosec: block.block.header.timestamp_nanosec, txs_per_shard: block.shards.map((shard) => ({ shard_id: shard.shard_id, tx_count: shard.chunk?.transactions.length ?? 0, })), total_txs: block.shards.reduce( (count, shard) => count + (shard.chunk?.transactions.length ?? 0), 0 ), }); ``` ## What is the latest indexed value for this exact key? - ID: `kv-latest-key` - API: `near.fastdata.kv.getLatestKey` - Service: `fastdata.kv` - Returns: `FastNearKvGetLatestKeyResponse` - Network: `mainnet` - Auth: `none` - Summary: Start narrow with KV FastData when you already know the contract, predecessor, and exact storage key. - Output keys: `entries[].current_account_id`, `entries[].predecessor_id`, `entries[].block_height`, `entries[].key`, `entries[].value` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when you already know the exact key and want the latest indexed value immediately. - Use the broader history or predecessor scans only after this exact-key lookup stops being enough. Response notes: - KV FastData keeps the exact indexed storage history question narrow and fast when you already know the key. - The raw response keeps the full entry list; the example snippet extracts the most informative first entry. Follow-ups: - If you need history for the same key, continue with near.fastdata.kv.getHistoryKey. - If you only know the predecessor and need discovery, continue with near.fastdata.kv.allByPredecessor. Related recipes: - `view-contract` Example inputs: ```json { "currentAccountId": "social.near", "predecessorId": "james.near", "key": "graph/follow/sleet.near" } ``` ### Terminal ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. node -e "$(curl -fsSL https://js.fastnear.com/agents.js)" <<'EOF' const result = await near.fastdata.kv.getLatestKey({ currentAccountId: "social.near", predecessorId: "james.near", key: "graph/follow/sleet.near", }); const latest = result.entries?.[0] || null; near.print({ latest: latest ? { current_account_id: latest.current_account_id, predecessor_id: latest.predecessor_id, block_height: latest.block_height, key: latest.key, value: latest.value, } : null, }); EOF ``` ### curl + jq ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. CURRENT_ACCOUNT_ID=social.near PREDECESSOR_ID=james.near KEY='graph/follow/sleet.near' ENCODED_KEY="$(jq -rn --arg key "$KEY" '$key | @uri')" curl -sS "https://kv.main.fastnear.com/v0/latest/$CURRENT_ACCOUNT_ID/$PREDECESSOR_ID/$ENCODED_KEY" -H "Authorization: Bearer $FASTNEAR_API_KEY" | jq '{ latest: ( .entries[0] | { current_account_id, predecessor_id, block_height, key, value } ) }' ``` ### Browser Global ```js const result = await near.fastdata.kv.getLatestKey({ currentAccountId: "social.near", predecessorId: "james.near", key: "graph/follow/sleet.near", }); const latest = result.entries?.[0] || null; near.print({ latest: latest ? { current_account_id: latest.current_account_id, predecessor_id: latest.predecessor_id, block_height: latest.block_height, key: latest.key, value: latest.value, } : null, }); ``` ### ESM ```js import * as near from "@fastnear/api"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); const result = await near.fastdata.kv.getLatestKey({ currentAccountId: "social.near", predecessorId: "james.near", key: "graph/follow/sleet.near", }); const latest = result.entries?.[0] || null; near.print({ latest: latest ? { current_account_id: latest.current_account_id, predecessor_id: latest.predecessor_id, block_height: latest.block_height, key: latest.key, value: latest.value, } : null, }); ``` ## How do I connect a wallet? - ID: `connect-wallet` - API: `near.recipes.connect` - Service: `wallet` - Returns: `{ accountId: string } | undefined` - Network: `mainnet` - Auth: `wallet` - Summary: Open the wallet picker and attach a signer to the FastNear runtime. - Output keys: `accountId` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when the task crosses from read-only inspection into user-approved signing. - Use it once per browser session before function calls, transfers, or message signatures. Response notes: - Wallet-backed recipes are browser-first because they need an interactive signer. - This recipe is the smallest explicit connect step before sending transactions or signing messages. Follow-ups: - After connecting, send one contract action with near.recipes.functionCall. - If the next step is a simple NEAR payment, continue with near.recipes.transfer. Related recipes: - `function-call` - `transfer` - `sign-message` - `sign-delegate-actions` Example inputs: ```json { "contractId": "berryclub.ek.near" } ``` ### Terminal ```bash # Browser wallet required. # Opening the wallet picker needs a browser environment. # Use the browser-global or ESM snippet for this task. ``` ### Browser Global ```js const result = await near.recipes.connect({ contractId: "berryclub.ek.near", }); near.print(result ?? near.selected()); ``` ### ESM ```js import * as near from "@fastnear/api"; import * as nearWallet from "@fastnear/wallet"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); near.useWallet(nearWallet); await nearWallet.restore({ network: "mainnet", contractId: "berryclub.ek.near", manifest: "./manifest.json", }); const result = await near.recipes.connect({ contractId: "berryclub.ek.near", }); near.print(result ?? near.selected()); ``` ## How do I send one function call? - ID: `function-call` - API: `near.recipes.functionCall` - Service: `wallet` - Returns: `WalletTransactionResult` - Network: `mainnet` - Auth: `wallet` - Summary: Sign and broadcast a single contract call with readable gas units. - Output keys: `transaction`, `outcomes`, `status` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when you need one contract call and already know the receiver, method, and args. - Prefer this recipe over the lower-level sendTx path when you want the smallest wallet-backed API surface. Response notes: - This is the thinnest wallet-backed transaction recipe and keeps the action declaration explicit. - The example uses readable unit conversion before handing the transaction to the runtime. Follow-ups: - If the user only needs a native NEAR transfer, continue with near.recipes.transfer. - If you want to preview the action before signing, use near.explain.tx on the same action list. Related recipes: - `connect-wallet` - `transfer` - `sign-message` Example inputs: ```json { "receiverId": "berryclub.ek.near", "methodName": "draw", "args": { "pixels": [ { "x": 10, "y": 20, "color": 65280 } ] }, "gas": "100 Tgas", "deposit": "0" } ``` ### Terminal ```bash # Browser wallet required. # Signing and broadcasting a contract call needs a wallet session. # Use the browser-global or ESM snippet for this task. ``` ### Browser Global ```js const cu = near.utils.convertUnit; const result = await near.recipes.functionCall({ receiverId: "berryclub.ek.near", methodName: "draw", args: { pixels: [{ x: 10, y: 20, color: 65280 }], }, gas: cu("100 Tgas"), deposit: "0", }); near.print(result); ``` ### ESM ```js import * as near from "@fastnear/api"; import * as nearWallet from "@fastnear/wallet"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); near.useWallet(nearWallet); await nearWallet.restore({ network: "mainnet", contractId: "berryclub.ek.near", manifest: "./manifest.json", }); const cu = near.utils.convertUnit; const result = await near.recipes.functionCall({ receiverId: "berryclub.ek.near", methodName: "draw", args: { pixels: [{ x: 10, y: 20, color: 65280 }], }, gas: cu("100 Tgas"), deposit: "0", }); near.print(result); ``` ## How do I transfer NEAR? - ID: `transfer` - API: `near.recipes.transfer` - Service: `wallet` - Returns: `WalletTransactionResult` - Network: `mainnet` - Auth: `wallet+deposit` - Summary: Send a simple NEAR transfer with a wallet-backed signature. - Output keys: `transaction`, `outcomes`, `status` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when the task is a plain NEAR payment and not a contract method call. - Use the function-call recipe instead when the receiver expects method args or custom gas settings. Response notes: - This recipe keeps a simple NEAR transfer readable without constructing the action list manually. - The transaction still goes through the same wallet-backed approval flow as other signing tasks. Follow-ups: - If you want to explain the transfer before sending it, use near.explain.tx with a Transfer action. - If the next task is another signed action, keep the same wallet session and continue with near.recipes.functionCall. Related recipes: - `connect-wallet` - `function-call` Example inputs: ```json { "receiverId": "escrow.ai.near", "amount": "0.1 NEAR" } ``` ### Terminal ```bash # Browser wallet required. # Sending NEAR needs a wallet-backed signature flow. # Use the browser-global or ESM snippet for this task. ``` ### Browser Global ```js const cu = near.utils.convertUnit; const result = await near.recipes.transfer({ receiverId: "escrow.ai.near", amount: cu("0.1 NEAR"), }); near.print(result); ``` ### ESM ```js import * as near from "@fastnear/api"; import * as nearWallet from "@fastnear/wallet"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); near.useWallet(nearWallet); await nearWallet.restore({ network: "mainnet", contractId: "berryclub.ek.near", manifest: "./manifest.json", }); const cu = near.utils.convertUnit; const result = await near.recipes.transfer({ receiverId: "escrow.ai.near", amount: cu("0.1 NEAR"), }); near.print(result); ``` ## How do I sign a message? - ID: `sign-message` - API: `near.recipes.signMessage` - Service: `wallet` - Returns: `WalletMessageSignatureResult` - Network: `mainnet` - Auth: `wallet` - Summary: Request a wallet-backed NEP-413 signature for an app message. - Output keys: `signature`, `accountId`, `publicKey` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when you need user-approved application auth without broadcasting a transaction. - Prefer this over functionCall or transfer when the task is strictly off-chain signing. Response notes: - This is the wallet-backed message-signing path for NEP-413 style app messages. - It stays separate from transaction recipes because no chain write is involved. Follow-ups: - If you need to connect the wallet first, start with near.recipes.connect. - If the flow turns into an on-chain action, move to near.recipes.functionCall or near.recipes.transfer. Related recipes: - `connect-wallet` - `sign-delegate-actions` Example inputs: ```json { "message": "Sign in to FastNear Berry Club" } ``` ### Terminal ```bash # Browser wallet required. # Message signing depends on a connected wallet provider. # Use the browser-global or ESM snippet for this task. ``` ### Browser Global ```js const result = await near.recipes.signMessage({ message: "Sign in to FastNear Berry Club", recipient: window.location.host, nonce: crypto.getRandomValues(new Uint8Array(32)), }); near.print(result); ``` ### ESM ```js import * as near from "@fastnear/api"; import * as nearWallet from "@fastnear/wallet"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); near.useWallet(nearWallet); await nearWallet.restore({ network: "mainnet", contractId: "berryclub.ek.near", manifest: "./manifest.json", }); const result = await near.recipes.signMessage({ message: "Sign in to FastNear Berry Club", recipient: window.location.host, nonce: crypto.getRandomValues(new Uint8Array(32)), }); near.print(result); ``` ## How do I sign delegate actions for gasless transactions? - ID: `sign-delegate-actions` - API: `nearWallet.signDelegateActions` - Service: `wallet` - Returns: `SignDelegateActionsResponse` - Network: `mainnet` - Auth: `wallet` - Summary: Sign NEP-366 delegate actions via the connected wallet so a relayer can submit them on-chain without the user paying gas. - Output keys: `signedDelegateActions[].borshSerializedBase64` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when building gasless or relay-based flows where a third party submits the transaction on the user's behalf. - Prefer sign-message when you only need an off-chain signature, or function-call when the user can pay gas directly. Response notes: - The canonical result is { borshSerializedBase64: string }; legacy structured delegates and bare base64 strings remain in the public union for compatibility. - Returns signed delegate actions that a relayer can submit on-chain, enabling gasless transactions for the user. - The wallet must support the signDelegateActions feature (check WalletFeatures.signDelegateActions). - Requests that include blockHeightTtl additionally require WalletFeatures.signDelegateActionsWithTtl. Follow-ups: - Submit the signed delegate actions through a relayer service to execute on-chain without the signer paying gas. - If the flow does not need a relayer, use near.recipes.functionCall for a standard wallet-signed transaction instead. Related recipes: - `connect-wallet` - `sign-message` - `function-call` Example inputs: ```json { "delegateActions": [ { "receiverId": "berryclub.ek.near", "actions": [ { "type": "FunctionCall", "methodName": "draw", "args": { "pixels": [ { "x": 10, "y": 20, "color": 65280 } ] }, "gas": "100 Tgas", "deposit": "0" } ] } ] } ``` ### Terminal ```bash # Browser wallet required. # Signing delegate actions depends on a connected wallet provider. # Use the browser-global or ESM snippet for this task. ``` ### Browser Global ```js const cu = near.utils.convertUnit; const result = await nearWallet.signDelegateActions({ delegateActions: [ { receiverId: "berryclub.ek.near", actions: [ { type: "FunctionCall", methodName: "draw", args: { pixels: [{ x: 10, y: 20, color: 65280 }] }, gas: cu("100 Tgas"), deposit: "0", }, ], }, ], }); near.print({ count: result.signedDelegateActions.length, first_delegate_borsh_base64: result.signedDelegateActions[0]?.borshSerializedBase64, }); ``` ### ESM ```js import * as near from "@fastnear/api"; import * as nearWallet from "@fastnear/wallet"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); near.useWallet(nearWallet); await nearWallet.restore({ network: "mainnet", contractId: "berryclub.ek.near", manifest: "./manifest.json", }); const cu = near.utils.convertUnit; const result = await nearWallet.signDelegateActions({ delegateActions: [ { receiverId: "berryclub.ek.near", actions: [ { type: "FunctionCall", methodName: "draw", args: { pixels: [{ x: 10, y: 20, color: 65280 }] }, gas: cu("100 Tgas"), deposit: "0", }, ], }, ], }); near.print({ count: result.signedDelegateActions.length, first_delegate_borsh_base64: result.signedDelegateActions[0]?.borshSerializedBase64, }); ``` ## How do I build and preview a transaction before signing? - ID: `explain-transaction` - API: `near.explain.tx` - Service: `api` - Returns: `ExplainedTransaction` - Network: `mainnet` - Auth: `none` - Summary: Declare actions with readable unit gas/deposit and get a stable JSON summary from near.explain.tx — no wallet, no network, no BigInt. - Output keys: `kind`, `signerId`, `receiverId`, `actionCount`, `actions` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this to sanity-check exactly what you are about to sign before handing actions to a wallet or sendTx. - Use near.explain.action for a single action, or near.utils.txToJson to JSON-serialize a full signed transaction object. Response notes: - near.explain.tx is a pure function — it never opens a wallet or hits the network. - Wide integers (gas, deposit, amounts) are decimal strings in and out; you never need BigInt to build or inspect a transaction. - Gas and deposit are echoed exactly as written, including unit strings like "100 Tgas" — convert with near.utils.convertUnit only if you need the yocto value. Follow-ups: - Sign and broadcast the same actions with near.recipes.functionCall (wallet) or sendTx (local key). - See the hosted /transactions explainer for the full construct-and-send flow. Related recipes: - `function-call` - `transfer` - `sign-delegate-actions` Example inputs: ```json { "signerId": "root.near", "receiverId": "berryclub.ek.near", "actions": [ { "type": "FunctionCall", "methodName": "draw", "args": { "pixels": [ { "x": 10, "y": 20, "color": 65280 } ] }, "gas": "100 Tgas", "deposit": "0" } ] } ``` ### Terminal ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. node -e "$(curl -fsSL https://js.fastnear.com/agents.js)" <<'EOF' // Build actions with readable units — string, number, or bigint all work. const actions = [ near.actions.functionCall({ methodName: "draw", args: { pixels: [{ x: 10, y: 20, color: 65280 }] }, gas: "100 Tgas", deposit: "0", }), ]; // Preview before signing: a pure, JSON-safe summary — no network, no wallet. // Wide integers stay decimal strings, so this never trips on BigInt. near.print(near.explain.tx({ signerId: "root.near", receiverId: "berryclub.ek.near", actions, })); EOF ``` ### Browser Global ```js // Build actions with readable units — string, number, or bigint all work. const actions = [ near.actions.functionCall({ methodName: "draw", args: { pixels: [{ x: 10, y: 20, color: 65280 }] }, gas: "100 Tgas", deposit: "0", }), ]; // Preview before signing: a pure, JSON-safe summary — no network, no wallet. // Wide integers stay decimal strings, so this never trips on BigInt. near.print(near.explain.tx({ signerId: "root.near", receiverId: "berryclub.ek.near", actions, })); ``` ### ESM ```js import * as near from "@fastnear/api"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); // Build actions with readable units — string, number, or bigint all work. const actions = [ near.actions.functionCall({ methodName: "draw", args: { pixels: [{ x: 10, y: 20, color: 65280 }] }, gas: "100 Tgas", deposit: "0", }), ]; // Preview before signing: a pure, JSON-safe summary — no network, no wallet. // Wide integers stay decimal strings, so this never trips on BigInt. near.print(near.explain.tx({ signerId: "root.near", receiverId: "berryclub.ek.near", actions, })); ``` ## What is this account's FT balance? - ID: `ft-balance` - API: `near.ft.balance` - Service: `rpc` - Returns: `string` - Network: `mainnet` - Auth: `none` - Summary: Read a NEP-141 token balance with a one-line wrapper that fills in the standard method name. - Output keys: `raw_balance`, `symbol`, `decimals`, `human_amount` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when you already know the FT contract and want one account's balance. - Use ft-inventory instead when you need every FT balance an account holds. Response notes: - near.ft.balance returns the raw integer balance string from ft_balance_of, scaled by the token's decimals. - Pair with near.ft.metadata to format the human-readable amount in one shot. Follow-ups: - If you need every token the account holds, switch to near.ft.inventory. - If you need a historical balance, add useArchival: true and a blockId — see archival-snapshot. Related recipes: - `ft-metadata` - `ft-inventory` - `archival-snapshot` Example inputs: ```json { "contractId": "berryclub.ek.near", "accountId": "root.near" } ``` ### Terminal ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. node -e "$(curl -fsSL https://js.fastnear.com/agents.js)" <<'EOF' const balance = await near.ft.balance({ contractId: "berryclub.ek.near", accountId: "root.near", }); const meta = await near.ft.metadata({ contractId: "berryclub.ek.near" }); near.print({ raw_balance: balance, symbol: meta.symbol, decimals: meta.decimals, human_amount: (Number(balance) / 10 ** meta.decimals).toFixed(meta.decimals), }); EOF ``` ### curl + jq ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. ACCOUNT_ID=root.near ARGS_BASE64="$(jq -nc --arg account_id "$ACCOUNT_ID" '{account_id: $account_id}' | base64 | tr -d '\n')" curl -sS "https://rpc.mainnet.fastnear.com?apiKey=$FASTNEAR_API_KEY" -H 'content-type: application/json' --data "$(jq -nc --arg args "$ARGS_BASE64" '{ jsonrpc:"2.0",id:"fastnear",method:"query", params:{ request_type:"call_function", finality:"final", account_id:"berryclub.ek.near", method_name:"ft_balance_of", args_base64:$args } }')" | jq -r '.result.result | implode' ``` ### Browser Global ```js const balance = await near.ft.balance({ contractId: "berryclub.ek.near", accountId: "root.near", }); const meta = await near.ft.metadata({ contractId: "berryclub.ek.near" }); near.print({ raw_balance: balance, symbol: meta.symbol, decimals: meta.decimals, human_amount: (Number(balance) / 10 ** meta.decimals).toFixed(meta.decimals), }); ``` ### ESM ```js import * as near from "@fastnear/api"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); const balance = await near.ft.balance({ contractId: "berryclub.ek.near", accountId: "root.near", }); const meta = await near.ft.metadata({ contractId: "berryclub.ek.near" }); near.print({ raw_balance: balance, symbol: meta.symbol, decimals: meta.decimals, human_amount: (Number(balance) / 10 ** meta.decimals).toFixed(meta.decimals), }); ``` ## What does this NEP-141 token call itself? - ID: `ft-metadata` - API: `near.ft.metadata` - Service: `rpc` - Returns: `{ name: string; symbol: string; decimals: number; icon?: string; reference?: string; reference_hash?: string }` - Network: `mainnet` - Auth: `none` - Summary: Fetch name, symbol, decimals, and icon for a fungible token in one call. - Output keys: `name`, `symbol`, `decimals`, `icon` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when you need to format a balance, label a token, or display branding. - Pair with ft-balance whenever you also need a human-readable amount. Response notes: - Wraps the standard NEP-141 ft_metadata view call so the method name does not have to be remembered. - Decimals comes back as a number; multiply or divide raw balances accordingly. Follow-ups: - If you also need an account balance, continue with near.ft.balance. - If you want a portfolio view across all tokens, use near.ft.inventory. Related recipes: - `ft-balance` - `ft-inventory` Example inputs: ```json { "contractId": "berryclub.ek.near" } ``` ### Terminal ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. node -e "$(curl -fsSL https://js.fastnear.com/agents.js)" <<'EOF' const meta = await near.ft.metadata({ contractId: "berryclub.ek.near", }); near.print({ name: meta.name, symbol: meta.symbol, decimals: meta.decimals, icon_present: !!meta.icon, }); EOF ``` ### curl + jq ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. EMPTY_ARGS_BASE64="$(printf '{}' | base64)" curl -sS "https://rpc.mainnet.fastnear.com?apiKey=$FASTNEAR_API_KEY" -H 'content-type: application/json' --data "$(jq -nc --arg args "$EMPTY_ARGS_BASE64" '{ jsonrpc:"2.0",id:"fastnear",method:"query", params:{ request_type:"call_function", finality:"final", account_id:"berryclub.ek.near", method_name:"ft_metadata", args_base64:$args } }')" | jq '.result.result | implode | fromjson | {name, symbol, decimals}' ``` ### Browser Global ```js const meta = await near.ft.metadata({ contractId: "berryclub.ek.near", }); near.print({ name: meta.name, symbol: meta.symbol, decimals: meta.decimals, icon_present: !!meta.icon, }); ``` ### ESM ```js import * as near from "@fastnear/api"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); const meta = await near.ft.metadata({ contractId: "berryclub.ek.near", }); near.print({ name: meta.name, symbol: meta.symbol, decimals: meta.decimals, icon_present: !!meta.icon, }); ``` ## Which fungible tokens does this account hold? - ID: `ft-inventory` - API: `near.ft.inventory` - Service: `api` - Returns: `{ tokens: Array<{ contract_id: string; balance: string; last_update_block_height?: number }> }` - Network: `mainnet` - Auth: `bearer` - Summary: List every NEP-141 contract the account holds via the FastNear indexer in one call. - Output keys: `ft_contract_count`, `preview[].contract_id`, `preview[].balance` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when the question is 'what tokens does this account own?' rather than one specific contract. - Use ft-balance instead when you already know the contract id. Response notes: - near.ft.inventory hits the FastNear indexer (api.fastnear.com) — set near.config({ apiKey }) to avoid the public rate limit. - Returns one row per FT contract the account currently holds; balances are raw integer strings. Follow-ups: - If you need NFTs as well, switch to near.nft.inventory. - If you need staking and aggregate state too, use near.api.v1.accountFull. Related recipes: - `ft-balance` - `nft-inventory` - `account-full` Example inputs: ```json { "accountId": "root.near" } ``` ### Terminal ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. node -e "$(curl -fsSL https://js.fastnear.com/agents.js)" <<'EOF' // Tip: `near.config({ apiKey: "" })` raises rate limits. const inventory = await near.ft.inventory({ accountId: "root.near", }); near.print({ ft_contract_count: inventory.tokens.length, preview: inventory.tokens.slice(0, 5).map((entry) => ({ contract_id: entry.contract_id, balance: entry.balance, })), }); EOF ``` ### curl + jq ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. ACCOUNT_ID=root.near curl -sS "https://api.fastnear.com/v1/account/$ACCOUNT_ID/ft" -H "Authorization: Bearer $FASTNEAR_API_KEY" | jq '{ ft_contract_count: (.tokens | length), preview: [.tokens[0:5][] | {contract_id, balance}] }' ``` ### Browser Global ```js // Tip: `near.config({ apiKey: "" })` raises rate limits. const inventory = await near.ft.inventory({ accountId: "root.near", }); near.print({ ft_contract_count: inventory.tokens.length, preview: inventory.tokens.slice(0, 5).map((entry) => ({ contract_id: entry.contract_id, balance: entry.balance, })), }); ``` ### ESM ```js import * as near from "@fastnear/api"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); // Tip: `near.config({ apiKey: "" })` raises rate limits. const inventory = await near.ft.inventory({ accountId: "root.near", }); near.print({ ft_contract_count: inventory.tokens.length, preview: inventory.tokens.slice(0, 5).map((entry) => ({ contract_id: entry.contract_id, balance: entry.balance, })), }); ``` ## Which NFTs does this account own on this contract? - ID: `nft-for-owner` - API: `near.nft.forOwner` - Service: `rpc` - Returns: `Array<{ token_id: string; owner_id: string; metadata?: { title?: string; media?: string; description?: string } }>` - Network: `mainnet` - Auth: `none` - Summary: List the tokens an account owns under one NEP-171 contract with metadata included. - Output keys: `count`, `preview[].token_id`, `preview[].title` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when you already know the NFT contract and want one account's collection on it. - Use nft-inventory instead when you want NFTs across every contract for an account. Response notes: - Wraps NEP-171 nft_tokens_for_owner; the contract decides what metadata fields to embed. - Pass from_index and limit (numbers, sometimes strings depending on the contract) for pagination. Follow-ups: - If you need contract-level metadata, call near.nft.metadata. - If you need cross-contract NFT discovery, switch to near.nft.inventory. Related recipes: - `nft-inventory` Example inputs: ```json { "contractId": "x.paras.near", "accountId": "root.near", "limit": 5 } ``` ### Terminal ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. node -e "$(curl -fsSL https://js.fastnear.com/agents.js)" <<'EOF' const tokens = await near.nft.forOwner({ contractId: "x.paras.near", accountId: "root.near", limit: 5, }); near.print({ count: tokens.length, preview: tokens.map((token) => ({ token_id: token.token_id, title: token.metadata?.title, })), }); EOF ``` ### curl + jq ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. ACCOUNT_ID=root.near ARGS_BASE64="$(jq -nc --arg account_id "$ACCOUNT_ID" '{account_id: $account_id, limit: 5}' | base64 | tr -d '\n')" curl -sS "https://rpc.mainnet.fastnear.com?apiKey=$FASTNEAR_API_KEY" -H 'content-type: application/json' --data "$(jq -nc --arg args "$ARGS_BASE64" '{ jsonrpc:"2.0",id:"fastnear",method:"query", params:{ request_type:"call_function", finality:"final", account_id:"x.paras.near", method_name:"nft_tokens_for_owner", args_base64:$args } }')" | jq '.result.result | implode | fromjson | [.[] | {token_id, title: .metadata.title}]' ``` ### Browser Global ```js const tokens = await near.nft.forOwner({ contractId: "x.paras.near", accountId: "root.near", limit: 5, }); near.print({ count: tokens.length, preview: tokens.map((token) => ({ token_id: token.token_id, title: token.metadata?.title, })), }); ``` ### ESM ```js import * as near from "@fastnear/api"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); const tokens = await near.nft.forOwner({ contractId: "x.paras.near", accountId: "root.near", limit: 5, }); near.print({ count: tokens.length, preview: tokens.map((token) => ({ token_id: token.token_id, title: token.metadata?.title, })), }); ``` ## Which NFT contracts does this account hold tokens on? - ID: `nft-inventory` - API: `near.nft.inventory` - Service: `api` - Returns: `{ tokens: Array<{ contract_id: string; last_update_block_height: number | null }> }` - Network: `mainnet` - Auth: `bearer` - Summary: Discover every NEP-171 contract the account holds tokens under via the FastNear indexer. - Output keys: `contract_count`, `preview[].contract_id`, `preview[].last_update_block_height` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when the question is 'what NFT contracts does this account hold tokens on?'. - Use nft-for-owner when you already know the specific contract id. Response notes: - near.nft.inventory hits the FastNear indexer — set near.config({ apiKey }) to avoid the public rate limit. - One row per NFT contract the account holds — contract ids only, no per-token ids; follow up with nft-for-owner per contract for token details. Follow-ups: - Drill into one contract with near.nft.forOwner for the per-token details. - Pull holdings + staking + native NEAR with near.api.v1.accountFull. Related recipes: - `nft-for-owner` - `ft-inventory` - `account-full` Example inputs: ```json { "accountId": "root.near" } ``` ### Terminal ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. node -e "$(curl -fsSL https://js.fastnear.com/agents.js)" <<'EOF' // Tip: `near.config({ apiKey: "" })` raises rate limits. const inventory = await near.nft.inventory({ accountId: "root.near", }); // The inventory endpoint returns contract-level rows only (no per-token // ids) — use the nft-for-owner recipe to list tokens on one contract. near.print({ contract_count: inventory.tokens.length, preview: inventory.tokens.slice(0, 3).map((entry) => ({ contract_id: entry.contract_id, last_update_block_height: entry.last_update_block_height, })), }); EOF ``` ### curl + jq ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. ACCOUNT_ID=root.near curl -sS "https://api.fastnear.com/v1/account/$ACCOUNT_ID/nft" -H "Authorization: Bearer $FASTNEAR_API_KEY" | jq '{ contract_count: (.tokens | length), preview: [.tokens[0:3][] | {contract_id, token_count: (.tokens // [] | length)}] }' ``` ### Browser Global ```js // Tip: `near.config({ apiKey: "" })` raises rate limits. const inventory = await near.nft.inventory({ accountId: "root.near", }); // The inventory endpoint returns contract-level rows only (no per-token // ids) — use the nft-for-owner recipe to list tokens on one contract. near.print({ contract_count: inventory.tokens.length, preview: inventory.tokens.slice(0, 3).map((entry) => ({ contract_id: entry.contract_id, last_update_block_height: entry.last_update_block_height, })), }); ``` ### ESM ```js import * as near from "@fastnear/api"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); // Tip: `near.config({ apiKey: "" })` raises rate limits. const inventory = await near.nft.inventory({ accountId: "root.near", }); // The inventory endpoint returns contract-level rows only (no per-token // ids) — use the nft-for-owner recipe to list tokens on one contract. near.print({ contract_count: inventory.tokens.length, preview: inventory.tokens.slice(0, 3).map((entry) => ({ contract_id: entry.contract_id, last_update_block_height: entry.last_update_block_height, })), }); ``` ## What did this account look like at a specific block? - ID: `archival-snapshot` - API: `near.queryAccount` - Service: `rpc` - Returns: `RpcViewAccountResponse` - Network: `mainnet` - Auth: `none` - Summary: Read canonical account state at a historical block by setting useArchival on the query. - Output keys: `amount`, `storage_usage`, `block_hash`, `block_height` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when blockId predates the regular RPC's retention window or you specifically want a historical snapshot. - Combine with the FT or NFT helpers (`useArchival: true` is forwarded) when you need a historical token balance. Response notes: - useArchival: true routes a single call to NEAR's archival RPC (archival-rpc.{mainnet,testnet}.near.org) which retains state past the regular RPC's ~5-epoch window. - The same flag works on near.view, near.queryAccount, near.queryBlock, near.queryAccessKey, near.queryTx, and the lower-level near.sendRpc — falls back to the regular RPC if archival isn't configured for the network. Follow-ups: - Pair with near.ft.balance({ blockId, useArchival: true }) for a historical token balance. - If you need a full holdings snapshot at a block, walk near.api.v1.accountFull with the blockId once it's supported on that surface. Related recipes: - `view-account` - `ft-balance` Example inputs: ```json { "accountId": "root.near", "blockId": 100000000, "useArchival": true } ``` ### Terminal ```bash # Assumes FASTNEAR_API_KEY is already set in your shell. node -e "$(curl -fsSL https://js.fastnear.com/agents.js)" <<'EOF' // Reads canonical account state at a specific historical block via the // archival RPC. Add useArchival to any view/query when blockId predates // the regular RPC's retention window (~5 epochs / a few days). const past = await near.queryAccount({ accountId: "root.near", blockId: 100_000_000, useArchival: true, }); // near.query* returns the raw JSON-RPC envelope — the data lives in .result. const acct = past.result; near.print({ amount: acct.amount, storage_usage: acct.storage_usage, block_hash: acct.block_hash, block_height: acct.block_height, }); EOF ``` ### curl + jq ```bash # NEAR's public archival RPC — no apiKey required. ACCOUNT_ID=root.near BLOCK_ID=100000000 curl -sS "https://archival-rpc.mainnet.near.org" -H 'content-type: application/json' --data "$(jq -nc --arg account_id "$ACCOUNT_ID" --argjson block "$BLOCK_ID" '{ jsonrpc:"2.0",id:"fastnear",method:"query", params:{request_type:"view_account",account_id:$account_id,block_id:$block} }')" | jq '.result | {amount, storage_usage, block_height, block_hash}' ``` ### Browser Global ```js // Reads canonical account state at a specific historical block via the // archival RPC. Add useArchival to any view/query when blockId predates // the regular RPC's retention window (~5 epochs / a few days). const past = await near.queryAccount({ accountId: "root.near", blockId: 100_000_000, useArchival: true, }); // near.query* returns the raw JSON-RPC envelope — the data lives in .result. const acct = past.result; near.print({ amount: acct.amount, storage_usage: acct.storage_usage, block_hash: acct.block_hash, block_height: acct.block_height, }); ``` ### ESM ```js import * as near from "@fastnear/api"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); // Reads canonical account state at a specific historical block via the // archival RPC. Add useArchival to any view/query when blockId predates // the regular RPC's retention window (~5 epochs / a few days). const past = await near.queryAccount({ accountId: "root.near", blockId: 100_000_000, useArchival: true, }); // near.query* returns the raw JSON-RPC envelope — the data lives in .result. const acct = past.result; near.print({ amount: acct.amount, storage_usage: acct.storage_usage, block_hash: acct.block_hash, block_height: acct.block_height, }); ``` ## How do I open a testnet wallet session alongside mainnet? - ID: `connect-testnet` - API: `near.recipes.connect` - Service: `wallet` - Returns: `{ accountId: string; network?: "mainnet" | "testnet" } | undefined` - Network: `testnet` - Auth: `wallet` - Summary: Use the per-network connect parameter to keep mainnet and testnet sessions side by side. - Output keys: `connected.accountId`, `connected.network`, `active_networks` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when the task spans both networks in the same page (mainnet read + testnet write, or A/B testing). - Use connect-wallet when one network at a time is enough. Response notes: - @fastnear/wallet 1.1.0 keys session state per network, so signing in on testnet does not evict an active mainnet session. - @fastnear/api 1.1.1 added a `network` parameter to near.recipes.connect (and signOut) — earlier versions silently used near.config().networkId. - nearWallet.connectedNetworks() returns the list of networks with an active session. Follow-ups: - Send a function call on the testnet contract with near.recipes.functionCall({ network: "testnet", … }) — see the function-call-testnet recipe. - Read the active session per network with nearWallet.accountId({ network: "testnet" }) and nearWallet.getActiveNetwork(). Related recipes: - `connect-wallet` - `function-call` - `function-call-testnet` Example inputs: ```json { "network": "testnet", "contractId": "guest-book.testnet" } ``` ### Terminal ```bash # Browser wallet required. # Opening the wallet picker needs a browser environment. # Use the browser-global or ESM snippet for this task. ``` ### Browser Global ```js // near.recipes.connect honors a per-call network override // (@fastnear/api 1.1.1+), so this opens a testnet session alongside any // existing mainnet session — wallet state is keyed per network. const result = await near.recipes.connect({ network: "testnet", contractId: "guest-book.testnet", }); near.print({ connected: result, active_networks: nearWallet.connectedNetworks(), }); ``` ### ESM ```js import * as near from "@fastnear/api"; import * as nearWallet from "@fastnear/wallet"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); near.useWallet(nearWallet); await nearWallet.restore({ network: "mainnet", contractId: "berryclub.ek.near", manifest: "./manifest.json", }); // near.recipes.connect honors a per-call network override // (@fastnear/api 1.1.1+), so this opens a testnet session alongside any // existing mainnet session — wallet state is keyed per network. const result = await near.recipes.connect({ network: "testnet", contractId: "guest-book.testnet", }); near.print({ connected: result, active_networks: nearWallet.connectedNetworks(), }); ``` ## How do I send a function call on testnet without losing my mainnet session? - ID: `function-call-testnet` - API: `near.recipes.functionCall` - Service: `wallet` - Returns: `{ outcomes?: any[] } | { rejected: true } | undefined` - Network: `testnet` - Auth: `wallet` - Summary: Pair near.recipes.connect({ network: "testnet" }) with near.recipes.functionCall({ network: "testnet" }) — connect honors the per-network override added in 1.1.1; functionCall does the same thanks to per-network account state in 1.1.2. - Output keys: `outcomes[].transaction.hash`, `outcomes[].status` - Pagination: none; request fields: none; response fields: none; filters must stay stable: no Choose this when: - Choose this when an action lives on testnet but the page also holds a live mainnet session. - Use function-call when only one network is involved. Response notes: - @fastnear/api 1.1.2 keys account state per network, so near.recipes.functionCall reads the testnet signer from its testnet slot regardless of which network is currently active. - Local-signing also honors the network override in 1.1.2 — the RPC helpers, queryAccessKey/queryBlock/sendTxToRpc, and the nonce/block caches are all keyed per network. - nearWallet.connectedNetworks() returns the list of networks with an active session if you want to gate the call on testnet being signed in first. Follow-ups: - Sign a NEP-413 message with the testnet session via near.recipes.signMessage(message, { network: "testnet" }). - Send NEAR with near.recipes.transfer({ network: "testnet", … }) — same per-network surface. Related recipes: - `connect-testnet` - `function-call` - `transfer` Example inputs: ```json { "network": "testnet", "receiverId": "guest-book.testnet", "methodName": "add_message", "args": { "text": "hello from a per-network call" }, "gas": "30000000000000", "deposit": "0" } ``` ### Terminal ```bash # Browser wallet required. # Signing a transaction needs the wallet picker, which needs a browser. # Use the browser-global or ESM snippet for this task. ``` ### Browser Global ```js // near.recipes.functionCall accepts a per-call network override // (@fastnear/api 1.1.2+) — once a testnet session is open, the api reads // the testnet account from its per-network state map and dispatches the // signed transaction through the wallet's testnet slot. The mainnet // session, if any, is untouched. await near.recipes.connect({ network: "testnet", contractId: "guest-book.testnet", }); const result = await near.recipes.functionCall({ network: "testnet", receiverId: "guest-book.testnet", methodName: "add_message", args: { text: "hello from a per-network call" }, gas: "30000000000000", deposit: "0", }); near.print(result); ``` ### ESM ```js import * as near from "@fastnear/api"; import * as nearWallet from "@fastnear/wallet"; near.config({ apiKey: process.env.FASTNEAR_API_KEY || undefined }); near.useWallet(nearWallet); await nearWallet.restore({ network: "mainnet", contractId: "berryclub.ek.near", manifest: "./manifest.json", }); // near.recipes.functionCall accepts a per-call network override // (@fastnear/api 1.1.2+) — once a testnet session is open, the api reads // the testnet account from its per-network state map and dispatches the // signed transaction through the wallet's testnet slot. The mainnet // session, if any, is untouched. await near.recipes.connect({ network: "testnet", contractId: "guest-book.testnet", }); const result = await near.recipes.functionCall({ network: "testnet", receiverId: "guest-book.testnet", methodName: "add_message", args: { text: "hello from a per-network call" }, gas: "30000000000000", deposit: "0", }); near.print(result); ``` ## Explain surface ### near.explain.action Normalize one action into a stable JSON summary. ```json { "kind": "action", "type": "FunctionCall", "methodName": "draw", "gas": "100000000000000", "deposit": "0", "args": { "pixels": [ { "x": 10, "y": 20, "color": 65280 } ] }, "argsBase64": null, "params": { "methodName": "draw", "gas": "100000000000000", "deposit": "0", "args": { "pixels": [ { "x": 10, "y": 20, "color": 65280 } ] }, "argsBase64": null } } ``` ### near.explain.tx Summarize a signer, receiver, and action list into stable JSON. ```json { "kind": "transaction", "signerId": "root.near", "receiverId": "berryclub.ek.near", "actionCount": 1, "actions": [ { "kind": "action", "type": "FunctionCall", "methodName": "draw", "gas": "100000000000000", "deposit": "0", "args": { "pixels": [ { "x": 10, "y": 20, "color": 65280 } ] }, "argsBase64": null, "params": { "methodName": "draw", "gas": "100000000000000", "deposit": "0", "args": { "pixels": [ { "x": 10, "y": 20, "color": 65280 } ] }, "argsBase64": null } } ] } ``` ### near.explain.error Turn thrown RPC, wallet, or transport failures into a predictable JSON object. ```json { "kind": "rpc_error", "code": -32000, "name": "FastNearError", "message": "Server error", "data": { "name": "HANDLER_ERROR" }, "retryable": true } ```