Skip to content

Algorand

Algorand is a pure-PoS, payments-focused L1: ~3s single-step finality with no reorgs, and sub-cent fees. Name chain: 'algorand' and the driver auto-mounts on first use — a pure-EVM install never downloads its library.

Assets are ASAs (Algorand Standard Assets), identified by a numeric asset id rather than a contract address; native ALGO and ASAs both transfer in integer base units. Every transaction carries an arbitrary note field (up to 1 KB), which is where the challenge nonce rides.

The Algorand library is an optional peer, lazy-loaded the first time you name chain: 'algorand':

Terminal window
npm install algosdk

An Algorand wallet is { key }, where key is a 25-word Algorand recovery phrase — or a ready { account } (an algosdk { addr, sk }, if you built the signer yourself):

import { PipRailClient } from '@piprail/sdk'
const client = new PipRailClient({
wallet: { key: process.env.ALGORAND_MNEMONIC! }, // 25 words
chain: 'algorand',
})

payTo is a 58-character Algorand address. See Wallets by family for every family’s shape, and PipRailClient for the full client surface.

Name a token by symbol, 'native', or a custom ASA:

tokenWhat it is
'native'Native ALGO (6 decimals, microAlgos). Zero-setup — no opt-in needed.
'USDC'Circle’s native USDC (ASA 31566704, 6dp) — verified live on mainnet before shipping.
{ assetId, decimals }Any other ASA, by numeric asset id.
import { requirePayment } from '@piprail/sdk'
app.get(
'/report',
requirePayment({
chain: 'algorand',
token: 'USDC',
amount: '0.10',
payTo: 'YourAlgoAddr', // 58-char Algorand address
}),
(req, res) => res.json({ ok: true }),
)

token: 'native' pays in ALGO and needs no opt-in at either end. It’s the simplest way to charge on Algorand: name the chain, name the amount, get paid.

import { requirePayment } from '@piprail/sdk'
app.get(
'/report',
requirePayment({
chain: 'algorand',
token: 'native',
amount: '0.10',
payTo: 'YourAlgoAddr',
}),
(req, res) => res.json({ ok: true }),
)

ALGO is the volatile gas coin, so for stable pricing pay in USDC; for no-setup flows, native ALGO is ideal.

Receiving USDC needs a one-time ASA opt-in

Section titled “Receiving USDC needs a one-time ASA opt-in”

Before an account can receive USDC — or any ASA — it must opt into that asset once: a 0-amount asset-transfer to itself, which raises its minimum balance by 0.1 ALGO (locked, and recoverable). Without it, the payment fails. The payer is implicitly opted-in if it already holds USDC.

planPayment() surfaces a recipient that hasn’t opted in as a RECIPIENT_NOT_READY blocker before you spend, and a payment to an unready recipient raises a RecipientNotReadyError:

const plan = await client.planPayment('https://api.example.com/report')
if (!plan) {
// not payment-gated — nothing to plan
} else if (!plan.payable) {
console.log(plan.fundingHint) // → "must opt into the USDC ASA" etc.
}

The opt-in is plain algosdk — PipRail stays a payments SDK, not a wallet manager. Run it once per account, per ASA:

import algosdk from 'algosdk'
const account = algosdk.mnemonicToSecretKey(process.env.ALGORAND_MNEMONIC!) // { addr, sk }
const algod = new algosdk.Algodv2('', 'https://mainnet-api.algonode.cloud', '')
const sp = await algod.getTransactionParams().do()
const optIn = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({
sender: account.addr,
receiver: account.addr,
amount: 0,
assetIndex: 31566704,
suggestedParams: sp,
})
await algod.sendRawTransaction(optIn.signTxn(account.sk)).do() // one-time, per account per ASA

The payer needs a little ALGO for gas on the onchain-proof rail — fees are a flat 0.001 ALGO. On the gasless exact rail (below) the payer needs no ALGO at all.

Gasless — the exact rail (the buyer pays zero ALGO)

Section titled “Gasless — the exact rail (the buyer pays zero ALGO)”

Beyond onchain-proof, Algorand supports the ratified x402 exact rail (opt-in), and it’s gasless for the buyer. Algorand has no per-account fee payer like Solana; instead a transaction group pools fees, so the buyer signs an ASA transfer at fee 0, atomically grouped with a 0-ALGO pay whose pooled fee covers the whole group. The sponsor — your own relayer, or a keyless facilitator — signs that fee txn and submits the group. The buyer spends zero ALGO, holding only USDC:

import { requirePayment } from '@piprail/sdk'
requirePayment({
chain: 'algorand', token: 'USDC', amount: '0.10', payTo: 'YourAlgoAddr',
exact: { settle: 'self', relayer: { key: process.env.ALGO_RELAYER_MNEMONIC! } }, // pools the group fee
})

Unlike Solana, the relayer may be payTo itself (the fee txn is separate — no isolation rule), so a single merchant account can self-settle. payTo must be opted into the ASA. Native ALGO isn’t exact-payable (the scheme is an ASA transfer) — it stays onchain-proof. Live-proven on Algorand mainnet. Full mechanism: Gasless payments → Algorand.

For truly gasless (neither buyer nor merchant pays), use the keyless GoPlausible facilitator — the only keyless Algorand x402 facilitator. Its sponsor pools the whole group fee, so the merchant pays 0 ALGO too:

requirePayment({
chain: 'algorand', token: 'USDC', amount: '0.10', payTo: 'YourAlgoAddr',
exact: true, // zero-config: auto-picks GoPlausible (or pin exact: { settle: { facilitator: 'https://facilitator.goplausible.xyz' } })
})

The gate reads GoPlausible’s sponsor address from its GET /supported, advertises it, and forwards verify+settle to GoPlausible — no relayer key needed. Live-settled on mainnet 2026-06-17 (tx PDVDVRFGJAG2K6AJ7L26OTSCSRL7AURVKEX4D4KHBAOLNSCYENXA), buyer and merchant both 0 ALGO. This makes Algorand the first non-EVM/non-Solana keyless PipRail chain. See Facilitator coverage.

Because the sponsor (GoPlausible, or your self-settle relayer) co-signs a buyer-built group, the gate caps the pooled group fee it will pay before co-signing (MAX_GROUP_FEE = 20 000 µALGO; the honest path is ~2 000 µALGO) and rejects any asset/account close or rekey — so a buyer can’t drain the sponsor on a sub-cent payment. See sponsor protection.

Algorand uses Template A: the challenge nonce rides in the transaction’s note field, so the proof is cryptographically bound to its challenge. verify() reads the merchant account’s inbound transfers via the indexer and re-derives every checked field from the trusted accept, never the client-supplied ref. See Verifying payments and Replay protection.

rpcUrl overrides the algod endpoint (used to submit transactions and fetch suggested params). The verify side reads inbound transfers from an indexer, which uses the public AlgoNode default — the public indexer is production-grade for this read, so overriding it is rarely necessary. The public algod is rate-limited; pass your own rpcUrl in production.