NEAR
Introduction
Section titled “Introduction”NEAR is the “user-owned AI” chain (its co-founder co-authored the Transformer paper), with both
Circle USDC and Tether USDT native on-chain. Name chain: 'near' and the driver auto-mounts
on first use — a pure-EVM install never downloads its library.
NEAR is unusual in PipRail: it uses both proof templates. Native NEAR is digest-bound (the
easy, zero-setup path); the NEP-141 token path is memo-bound and needs a one-time
storage_deposit. Both are covered below.
Install the peer dependency
Section titled “Install the peer dependency”The NEAR library is an optional peer, lazy-loaded the first time you name chain: 'near':
npm install near-api-jsWallet shape
Section titled “Wallet shape”A NEAR wallet is { accountId, privateKey } — NEAR signing needs both an account id and an
ed25519:… secret key (not just a private key). The presence of accountId is what tells the
SDK this is a NEAR wallet.
import { PipRailClient } from '@piprail/sdk'
const client = new PipRailClient({ wallet: { accountId: 'agent.near', privateKey: process.env.AGENT_KEY }, // ed25519:… secret chain: 'near',})payTo is a NEAR account id — a named account like merchant.near, or a 64-hex implicit
account. See Wallets by family for every family’s shape.
Supported tokens
Section titled “Supported tokens”Name a token by symbol, 'native', or a custom NEP-141 contract:
token | What it is |
|---|---|
'native' | Native NEAR (24 decimals). Zero-setup — digest-bound, no storage_deposit. |
'USDC' | Circle’s native USDC (17208628…36133a1, 6dp) — not the bridged …factory.bridge.near (USDC.e). |
'USDT' | Tether’s native USDt (usdt.tether-token.near, 6dp). |
{ contractId, decimals } | Any other NEP-141 token, by contract account id. |
import { requirePayment } from '@piprail/sdk'
// Express/Connect middleware that turns this route paid-only:requirePayment({ chain: 'near', token: 'USDC', amount: '0.10', payTo: 'merchant.near' })NEAR is the volatile gas coin, so for stable pricing pay in USDC/USDT; for no-setup flows, native NEAR is ideal.
Native NEAR — the zero-setup path
Section titled “Native NEAR — the zero-setup path”token: 'native' pays in NEAR via a plain Transfer, digest-bound like EVM/Solana/Sui:
the proof is <accountId>:<txHash>, verified by tx hash + a recency window + the gate’s
single-use proof set. Native needs no storage_deposit and a transfer even creates a
fresh implicit recipient — there is nothing to register first.
requirePayment({ chain: 'near', token: 'native', amount: '0.10', payTo: 'merchant.near' })Tokens need storage_deposit (the receive prerequisite)
Section titled “Tokens need storage_deposit (the receive prerequisite)”Before an account can receive a NEP-141 token it must be storage-registered on that exact
token contract (NEP-145) — a one-time ~0.00125 NEAR call, per account per token. Both the
merchant (payTo) and the payer must be registered, or the payer’s ft_transfer
panics.
planPayment() surfaces an unregistered recipient as a
RECIPIENT_NOT_READY blocker before you spend, and a payment to an unready recipient raises a
RecipientNotReadyError:
const url = 'https://api.example.com/report'const plan = await client.planPayment(url) // → PaymentPlan | null (null when not 402-gated)
if (!plan) { await client.fetch(url) // not payment-gated — just fetch it} else if (plan.payable) { await client.fetch(url) // safe — we checked} else { console.log(plan.fundingHint) // e.g. "recipient isn't registered on usdt.tether-token.near"}The payer needs a little NEAR for gas either way — native or token.
When a payment can’t go through
Section titled “When a payment can’t go through”client.fetch(url) pays the cheapest settleable rail. If the wallet or recipient isn’t ready it
throws a typed PipRailError — branch on the stable .code rather than
the message. On NEAR the two you’ll meet are RECIPIENT_NOT_READY (the payTo account isn’t
storage_deposit-registered on the token) and INSUFFICIENT_FUNDS (the payer is short on the
token or on NEAR for gas):
import { RecipientNotReadyError, InsufficientFundsError,} from '@piprail/sdk'
try { const res = await client.fetch(url) // → a normal Response once the proof verifies (200 + the gated resource)} catch (err) { if (err instanceof RecipientNotReadyError) { // fix the RECIPIENT: storage_deposit-register payTo on the token (~0.00125 NEAR) console.error('recipient not ready:', err.message) } else if (err instanceof InsufficientFundsError) { // fix the PAYER: top up the token, or add NEAR for gas console.error('payer is short:', err.message) } else { throw err }}Proof binding — both templates
Section titled “Proof binding — both templates”NEAR is the one family that uses both proof templates:
| Asset | Template | How it’s bound |
|---|---|---|
| Native NEAR | B — digest-bound | proof <accountId>:<txHash>, verified by tx hash + recency + single-use set |
| NEP-141 tokens | A — memo-bound | the challenge nonce rides in the ft_transfer memo |
NEAR has no account-history RPC, so the token path verifies by tx hash and only trusts an
ft_transfer event emitted by the real token contract — verify() re-derives every checked
field from the trusted accept, never the client-supplied ref. See Replay
protection for the single-use proof set.