@piprail/sdk 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHAINS.md CHANGED
@@ -13,7 +13,7 @@ read those sections before you ship them.
13
13
 
14
14
  | Chain(s) | Pay in native coin? | Built-in stablecoins | Receiver needs setup? | Wallet input |
15
15
  |---|:--:|---|---|---|
16
- | **EVM** (Ethereum, Base, Arbitrum, Optimism, Polygon, BNB, Avalanche, Mantle, Sonic, Linea, Scroll, Celo, zkSync, Unichain, World Chain, Sei, Injective, + any EVM chain) | ✅ ETH/BNB/POL/… | USDC (all) · USDT (all **except Base, World Chain, Sei**) | No | `{ privateKey }` |
16
+ | **EVM** (Ethereum, Base, Arbitrum, Optimism, Polygon, BNB, Avalanche, Mantle, Sonic, Linea, Scroll, Celo, zkSync, Unichain, World Chain, Sei, Injective, HyperEVM, Monad, + any EVM chain) | ✅ ETH/BNB/POL/… | USDC (all) · USDT (all **except Base, World Chain, Sei, HyperEVM, Monad**) | No | `{ privateKey }` |
17
17
  | **Solana** | ✅ SOL | USDC · USDT | No (payer creates the recipient's token account) | `{ secretKey }` |
18
18
  | **Sui** | ✅ SUI | USDC (no USDT) | No | `{ privateKey }` (`suiprivkey1…`) |
19
19
  | **Aptos** | ✅ APT | USDC · USDT | No (primary FA store auto-creates) | `{ privateKey }` (`ed25519-priv-0x…`) |
@@ -45,7 +45,7 @@ read those sections before you ship them.
45
45
 
46
46
  ### EVM — Ethereum, Base, Arbitrum, Optimism, Polygon, BNB, Avalanche, …
47
47
  - **Pay in:** native coin (`'native'`), `'USDC'`, `'USDT'`, or a custom `{ address, decimals }`.
48
- - **USDT gap:** built in on every preset **except Base, World Chain, and Sei** (USDC only there).
48
+ - **USDT gap:** built in on every preset **except Base, World Chain, Sei, HyperEVM, and Monad** (USDC only there).
49
49
  - **Decimals:** on **BNB Chain**, Binance-Peg USDC/USDT are **18 decimals**, not 6 (the SDK handles it; don't hardcode 6).
50
50
  - **USDT branding:** on **Arbitrum, Polygon, and Unichain** the canonical Tether is the omnichain **USD₮0 / USDT0** (LayerZero), and on **Celo** it's native **USD₮** — all genuine, Tether-issued, 6-decimal USDT at the addresses shipped (verified live on-chain by symbol/decimals/supply). You still ask for it as `token: 'USDT'`; only the on-chain `symbol()` string differs from the plain `USDT` your wallet may show elsewhere.
51
51
  - **Receiver setup:** none — any `0x…` address receives ERC-20 or native immediately.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,39 @@ All notable changes to `@piprail/sdk` are documented here. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
5
5
  versions follow [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [1.6.0] — 2026-06-05
8
+
9
+ ### Added
10
+ - **`policy.tokens` accepts `'native'`** — a chain-agnostic alias that allows the chain's native coin
11
+ (ETH/BNB/TRX/XLM/…) by the same word the accept side already uses (`token: 'native'`), without naming
12
+ the per-chain ticker. It's matched on the asset (not the symbol), so it works on every family; symbol
13
+ matching is unchanged (the real ticker still works), and `'native'` only ever matches a genuinely
14
+ native asset — it never loosens a stablecoin allowlist. Closes a terminology gap where allowing native
15
+ payments previously required knowing the coin's symbol. `@piprail/mcp`'s `PIPRAIL_TOKENS` inherits this.
16
+ Additive + non-breaking (next release is a minor).
17
+
18
+ ## [1.5.1] — 2026-06-04
19
+
20
+ **Cosmetic polish — docs & comments only, zero behavior change.** A repo-wide tidy pass so the
21
+ in-code docs match the SDK as it actually ships (10 families / 28 chains). No runtime, API, type,
22
+ or wire change — every existing program behaves identically.
23
+
24
+ - **JSDoc parity across the public surface.** The `chain` / `token` / `payTo` / wallet docs on
25
+ `RequirePaymentOptions`, `AcceptOption`, and `PipRailClientOptions` now enumerate all 10 families
26
+ (Aptos + Algorand were missing); the typed error JSDoc (`WrongFamilyError`, `UnknownTokenError`,
27
+ `MissingDriverError`, `RecipientNotReadyError`) lists every family + install command + custom-token form.
28
+ - **Stale comments corrected.** Native TRX and native NEAR are documented as the payment assets they've
29
+ been since 1.1.0 (the old "not a payment asset" / "`'native'` is rejected" notes were removed); the
30
+ `'native'` coin list, the barrel header, the tsup code-split note, and the lazy-mount docs now name all
31
+ 9 non-EVM families; the `paymentTools` doc says "three tools" (quote · plan · pay).
32
+ - **Driver-family symmetry.** `evm/wallet.ts` gained the `── EVM SECTION: wallet ──` banner the other 9
33
+ families carry, and `evm/index.ts`'s `recipientReady()` comment now uses the shared "No receive
34
+ prerequisite —" lead-in.
35
+ - **Docs:** README contract-method list adds `balanceOf` / `recipientReady`; README custom-token examples
36
+ add Aptos + Algorand; CHAINS.md lists HyperEVM + Monad (and their USDT gap); ERRORS.md + AGENTS.md list
37
+ all 10 families; CHANGELOG version footer links restored.
38
+ - **Packaging:** `algosdk` moved to its alphabetical slot in `peerDependencies` (no dependency change).
39
+
7
40
  ## [1.5.0] — 2026-06-04
8
41
 
9
42
  **The killer agent feature — `client.planPayment(url)`.** A read-only call that surveys a 402
@@ -384,6 +417,14 @@ straight into your wallet. The API is small and self-contained.
384
417
  to your wallet; PipRail never holds funds.
385
418
  - `viem ^2.21` is a peer dependency. Node 20+ or a modern browser.
386
419
 
420
+ [1.6.0]: https://www.npmjs.com/package/@piprail/sdk
421
+ [1.5.1]: https://www.npmjs.com/package/@piprail/sdk
422
+ [1.5.0]: https://www.npmjs.com/package/@piprail/sdk
423
+ [1.4.0]: https://www.npmjs.com/package/@piprail/sdk
424
+ [1.3.1]: https://www.npmjs.com/package/@piprail/sdk
425
+ [1.3.0]: https://www.npmjs.com/package/@piprail/sdk
426
+ [1.2.0]: https://www.npmjs.com/package/@piprail/sdk
427
+ [1.1.1]: https://www.npmjs.com/package/@piprail/sdk
387
428
  [1.1.0]: https://www.npmjs.com/package/@piprail/sdk
388
429
  [1.0.0]: https://www.npmjs.com/package/@piprail/sdk
389
430
  [0.1.0]: https://www.npmjs.com/package/@piprail/sdk
package/ERRORS.md CHANGED
@@ -205,9 +205,10 @@ the reader, full raw detail for the debugger — both, always. Chains with no re
205
205
 
206
206
  ## 7. Registry / loader pattern
207
207
 
208
- - EVM is registered eagerly (`viem` is the one hard peer dep). Solana / TON / Stellar mount
209
- lazily via a single dynamic `import()` in [`drivers/index.ts`](src/drivers/index.ts) the
210
- first time their `chain` is named — no setup call.
208
+ - EVM is registered eagerly (`viem` is the one hard peer dep). Every non-EVM family (Solana,
209
+ TON, Tron, NEAR, Sui, Stellar, XRPL, Aptos, Algorand) mounts lazily via a single dynamic
210
+ `import()` in [`drivers/index.ts`](src/drivers/index.ts) the first time its `chain` is
211
+ named — no setup call.
211
212
  - A failed lazy `import()` → `MissingDriverError` naming the exact `npm install` + `{ cause }`.
212
213
  The in-flight promise isn't cached on failure, so a later call can retry.
213
214
  - No driver for the family, or `resolve()` → `null` → `UnsupportedNetworkError`.
package/README.md CHANGED
@@ -52,7 +52,7 @@ const client = new PipRailClient({
52
52
  maxAmount: '0.10', // never pay more than $0.10 for one call
53
53
  maxTotal: '5.00', // never spend more than $5 total (per token)
54
54
  chains: ['base'], // only on Base
55
- tokens: ['USDC'], // only in USDC
55
+ tokens: ['USDC'], // only in USDC (use 'native' to also allow the chain's coin)
56
56
  hosts: ['*.example.com'], // only these hosts
57
57
  },
58
58
  onBeforePay: (q) => Number(q.amountFormatted) <= 0.05, // final say on each payment
@@ -75,6 +75,8 @@ client.spent() // → { count, byAsset: [{ symbol:'USDC', totalFormatted:'0.05',
75
75
 
76
76
  **The budget can't be fooled.** `maxAmount`/`maxTotal` are enforced against the token's **true** decimals (the SDK's own, via the driver) — a server can't slip past a cap by understating the price, and an asset the SDK can't recognise is refused unless you set `allowUnknownTokens`. `quote()` even flags a `symbolMismatch` when a challenge's stated symbol disagrees with the real token.
77
77
 
78
+ **`policy.tokens` takes symbols *or* `'native'`.** List stablecoin symbols (`'USDC'`, `'USDT'`, …) and/or the chain-agnostic alias **`'native'`** to allow the chain's own coin (ETH/BNB/TRX/XLM/…) on any family — the same word the accept side uses (`token: 'native'`), so you never name per-chain tickers (the real ticker works too). It only ever matches a genuinely native asset, so it never loosens a stablecoin-only list. The MCP server's `PIPRAIL_TOKENS` is the same allowlist.
79
+
78
80
  **Know the gas before you pay.** `client.estimateCost(url)` returns the quote **and** a `CostEstimate` — the network fee in the chain's **native coin** (you pay in USDC but burn ETH / SOL / TON / XLM / XRP / TRX on gas, a separate balance the agent must keep topped up). It's best-effort and labelled (`cost.basis`): a live-RPC read where cheap (`'estimated'` — EVM gas price, XRPL fee), a typical-cost constant otherwise (`'heuristic'`), and it never throws. Most valuable on **Tron**, where a USD₮ transfer can cost real TRX. So an agent can budget the *total* — payment **+** gas — before any funds move. Every driver implements it; the math is extracted per-chain and shaped uniformly by one shared `nativeCost()` helper.
79
81
 
80
82
  ### Plan before you pay — `planPayment()` (never fumble a payment)
@@ -276,6 +278,12 @@ requirePayment({ chain: 'near', token: { contractId: 'token.near', decimals: 6 }
276
278
 
277
279
  // On Sui, a custom coin is { coinType, decimals }:
278
280
  requirePayment({ chain: 'sui', token: { coinType: '0x…::usdc::USDC', decimals: 6 }, amount: '0.05', payTo })
281
+
282
+ // On Aptos, a custom Fungible Asset is { metadata, decimals }:
283
+ requirePayment({ chain: 'aptos', token: { metadata: '0x…', decimals: 6 }, amount: '0.05', payTo })
284
+
285
+ // On Algorand, a custom ASA is { assetId, decimals }:
286
+ requirePayment({ chain: 'algorand', token: { assetId: 12345678, decimals: 6 }, amount: '0.05', payTo })
279
287
  ```
280
288
 
281
289
  > **Production:** the built-in chains use public RPCs (rate-limited). Pass your own `rpcUrl` for real traffic.
@@ -603,7 +611,7 @@ Methods: `fetch` · `get` · `post` (return the gated `Response` after settlemen
603
611
 
604
612
  **Hand an LLM a wallet:** `paymentTools(client)` → framework-agnostic tool descriptors (MCP / AI SDK / function-calling), budget enforced by the client.
605
613
 
606
- **Bring your own chain family:** the SDK is built on a tiny `PaymentDriver` contract — `resolve(chain)` returns a bound network with `resolveToken` / `describeAsset` / `assertValidPayTo` / `bindWallet` / `send` / `confirm` / `estimateCost` / `verify`. Register your own with `registerDriver(...)`; the protocol layer never changes (see [Architecture](#architecture-under-the-hood)).
614
+ **Bring your own chain family:** the SDK is built on a tiny `PaymentDriver` contract — `resolve(chain)` returns a bound network with `resolveToken` / `describeAsset` / `assertValidPayTo` / `bindWallet` / `send` / `confirm` / `estimateCost` / `balanceOf` / `recipientReady` / `verify`. Register your own with `registerDriver(...)`; the protocol layer never changes (see [Architecture](#architecture-under-the-hood)).
607
615
 
608
616
  **Universal x402 (experimental):** building blocks to pay servers on the mainstream x402 `exact` scheme (EIP-3009 + facilitator) — `parseExactRequirements`, `buildExactAuthorization`, `encodeXPaymentHeader`. EVM-only; validate against your target facilitator before production.
609
617
 
package/dist/index.cjs CHANGED
@@ -733,7 +733,7 @@ function makeEvmNetwork(resolved) {
733
733
  }
734
734
  return { token, native };
735
735
  },
736
- // EVM has no receive prerequisite — any 0x address receives native or ERC-20 immediately.
736
+ // No receive prerequisite — any 0x address receives native or ERC-20 immediately.
737
737
  async recipientReady() {
738
738
  return { ready: "n/a" };
739
739
  },
@@ -923,7 +923,12 @@ function evaluatePolicy(intent, policy, spentForAssetBase) {
923
923
  }
924
924
  if (policy.tokens) {
925
925
  const sym = intent.recognized ? intent.symbol : void 0;
926
- if (!sym || !policy.tokens.some((t) => t.toUpperCase() === sym.toUpperCase())) {
926
+ const isNative = intent.asset === "native";
927
+ const matches = policy.tokens.some((t) => {
928
+ if (isNative && t.toUpperCase() === "NATIVE") return true;
929
+ return sym ? t.toUpperCase() === sym.toUpperCase() : false;
930
+ });
931
+ if (!matches) {
927
932
  return deny(
928
933
  `token ${_nullishCoalesce(intent.symbol, () => ( intent.asset))} is not in the allowed set (policy.tokens).`
929
934
  );
package/dist/index.d.cts CHANGED
@@ -3845,7 +3845,7 @@ interface AlgorandToken {
3845
3845
  }
3846
3846
  /**
3847
3847
  * What to be paid in. Each driver validates the forms it accepts:
3848
- * - 'native' the chain's native coin (ETH, BNB, SOL, TON, XLM, XRP)
3848
+ * - 'native' the chain's native coin (ETH, BNB, SOL, TON, XLM, XRP, TRX, NEAR, SUI, APT, ALGO)
3849
3849
  * - 'USDC' (string) a symbol resolved against the chosen chain
3850
3850
  * - EvmToken any ERC-20 (EVM chains)
3851
3851
  * - SolanaToken any SPL token (Solana)
@@ -4014,8 +4014,11 @@ interface PaymentPolicy {
4014
4014
  /** Allowlist of chains the agent may pay on. A 402 on any other chain is
4015
4015
  * declined. Strings match the configured selector; objects match by id. */
4016
4016
  chains?: ChainSelector[];
4017
- /** Allowlist of token symbols (matched against the TRUE symbol). An asset the
4018
- * SDK can't recognise never satisfies this list. */
4017
+ /** Allowlist of token symbols (matched against the TRUE symbol). The special
4018
+ * value `'native'` is a chain-agnostic alias for the chain's native coin — it
4019
+ * matches the native asset on ANY family (ETH/BNB/TRX/XLM/…) without naming
4020
+ * the ticker, mirroring the merchant-side `token: 'native'`. An asset the SDK
4021
+ * can't recognise never satisfies this list. */
4019
4022
  tokens?: string[];
4020
4023
  /** Allowlist of hosts. Exact (`api.example.com`) or wildcard (`*.example.com`). */
4021
4024
  hosts?: string[];
@@ -4130,6 +4133,8 @@ type PipRailEvent = {
4130
4133
  * - Stellar → `{ secret }` (S… seed) or a ready `{ keypair }`
4131
4134
  * - XRPL → `{ seed }` (s… seed) or a ready `{ wallet }`
4132
4135
  * - NEAR → `{ accountId, privateKey }` (privateKey = ed25519:… secret)
4136
+ * - Aptos → `{ privateKey }` (AIP-80 `ed25519-priv-0x…` / raw `0x…` hex) or `{ account }`
4137
+ * - Algorand→ `{ mnemonic }` (25 words) or a ready `{ account }`
4133
4138
  */
4134
4139
  type WalletInput = {
4135
4140
  privateKey: string;
@@ -4263,7 +4268,7 @@ interface PipRailClientOptions {
4263
4268
  /** Wallet for the chosen chain family. */
4264
4269
  wallet: WalletInput;
4265
4270
  /** Which chain to pay on. EVM ('bnb'|'base'|…), 'solana', 'ton', 'stellar',
4266
- * 'xrpl', 'tron', 'sui', or 'near'. */
4271
+ * 'xrpl', 'tron', 'sui', 'near', 'aptos', or 'algorand'. */
4267
4272
  chain: ChainSelector;
4268
4273
  /** Override the chain's default RPC URL (recommended in production). */
4269
4274
  rpcUrl?: string;
@@ -4441,8 +4446,10 @@ interface AgentTool {
4441
4446
  invoke: (args: Record<string, unknown>) => Promise<unknown>;
4442
4447
  }
4443
4448
  /**
4444
- * Two tools wrapping a configured {@link PipRailClient}:
4449
+ * Three tools wrapping a configured {@link PipRailClient}:
4445
4450
  * - `piprail_quote_payment(url)` — price a gated URL WITHOUT paying.
4451
+ * - `piprail_plan_payment(url)` — check you CAN pay (balance + gas + recipient
4452
+ * readiness) across every rail the URL offers, WITHOUT paying.
4446
4453
  * - `piprail_pay_request(url, method?, body?)` — pay if needed and return the result.
4447
4454
  *
4448
4455
  * A policy/approval refusal comes back as a structured `{ declined: true, reason }`
@@ -4478,7 +4485,7 @@ declare function paymentTools(client: PipRailClient): AgentTool[];
4478
4485
  */
4479
4486
  interface AcceptOption {
4480
4487
  /** Which chain. EVM ('bnb'|'base'|…), 'solana', 'ton', 'stellar', 'xrpl',
4481
- * 'tron', 'near', or 'sui'. */
4488
+ * 'tron', 'near', 'sui', 'aptos', or 'algorand'. */
4482
4489
  chain: ChainSelector;
4483
4490
  /** Token to be paid in (symbol / 'native' / custom descriptor). */
4484
4491
  token: TokenInput;
@@ -4492,8 +4499,9 @@ interface AcceptOption {
4492
4499
  interface RequirePaymentOptions {
4493
4500
  /**
4494
4501
  * Single-chain form: which chain to accept payment on. EVM ('bnb'|'base'|…),
4495
- * 'solana', 'ton', 'stellar', 'xrpl', 'tron', 'near', or 'sui'. Provide
4496
- * `chain` + `token` + `amount`, OR use the multi-chain `accept` array below.
4502
+ * 'solana', 'ton', 'stellar', 'xrpl', 'tron', 'near', 'sui', 'aptos', or
4503
+ * 'algorand'. Provide `chain` + `token` + `amount`, OR use the multi-chain
4504
+ * `accept` array below.
4497
4505
  */
4498
4506
  chain?: ChainSelector;
4499
4507
  /** Override the chain's default RPC URL (recommended in production). */
@@ -4504,9 +4512,10 @@ interface RequirePaymentOptions {
4504
4512
  * `{ address, decimals }` on EVM/Tron, `{ mint, decimals }` on Solana,
4505
4513
  * `{ master, decimals }` on TON, `{ issuer, code, decimals }` on Stellar,
4506
4514
  * `{ issuer, currencyHex, decimals }` on XRPL, `{ contractId, decimals }` on
4507
- * NEAR, `{ coinType, decimals }` on Sui. You name the token; the SDK fills in
4515
+ * NEAR, `{ coinType, decimals }` on Sui, `{ metadata, decimals }` on Aptos, or
4516
+ * `{ assetId, decimals }` on Algorand. You name the token; the SDK fills in
4508
4517
  * the contract + decimals for built-in symbols. (Note: native USDC doesn't
4509
- * exist on TON/Tron — USDT does; NEAR is FT-only, so `'native'` is rejected.)
4518
+ * exist on TON/Tron — USDT does; native NEAR is supported via `'native'`.)
4510
4519
  */
4511
4520
  token?: TokenInput;
4512
4521
  /** Human-readable amount, e.g. "0.05" (single-chain form). */
@@ -4518,8 +4527,8 @@ interface RequirePaymentOptions {
4518
4527
  */
4519
4528
  accept?: AcceptOption[];
4520
4529
  /** Address that receives the payment (0x… EVM/Sui, base58 Solana, EQ…/UQ… TON,
4521
- * G… Stellar, r… XRPL, T… Tron, account id on NEAR). Required for the single
4522
- * form; the per-option fallback for the multi form. */
4530
+ * G… Stellar, r… XRPL, T… Tron, account id on NEAR, 0x… Aptos, base32 Algorand).
4531
+ * Required for the single form; the per-option fallback for the multi form. */
4523
4532
  payTo?: AddressId;
4524
4533
  /** Shown to the agent in the challenge. */
4525
4534
  description?: string;
@@ -4669,7 +4678,7 @@ declare class InsufficientFundsError extends PipRailError {
4669
4678
  * The message states the requirement and the fix in plain language **and echoes
4670
4679
  * the raw chain code** (e.g. `(XRPL: tecNO_DST_INSUF_XRP)`), while the untouched
4671
4680
  * chain error is preserved on `.cause` for deeper debugging. Chains with no
4672
- * receive prerequisite (EVM, Solana, Sui, Tron, and native TON/NEAR) never throw it.
4681
+ * receive prerequisite (EVM, Solana, Sui, Aptos, Tron, and native TON/NEAR) never throw it.
4673
4682
  */
4674
4683
  declare class RecipientNotReadyError extends PipRailError {
4675
4684
  readonly code = "RECIPIENT_NOT_READY";
@@ -4766,7 +4775,7 @@ declare class NonReplayableBodyError extends PipRailError {
4766
4775
  }
4767
4776
  /**
4768
4777
  * The chosen chain belongs to one family (EVM, Solana, TON, Stellar, XRPL, Tron,
4769
- * Sui, NEAR) but the wallet, payTo, or token was given in another family's form
4778
+ * Sui, NEAR, Aptos, Algorand) but the wallet, payTo, or token was given in another family's form
4770
4779
  * (e.g. an `0x…` payTo on Solana, or a `{ mint }` token on a Stellar chain).
4771
4780
  */
4772
4781
  declare class WrongFamilyError extends PipRailError {
@@ -4778,7 +4787,8 @@ declare class WrongFamilyError extends PipRailError {
4778
4787
  * token by full descriptor ({ address, decimals } EVM/Tron · { mint, decimals }
4779
4788
  * Solana · { master, decimals } TON · { issuer, code, decimals } Stellar ·
4780
4789
  * { issuer, currencyHex, decimals } XRPL · { coinType, decimals } Sui ·
4781
- * { contractId, decimals } NEAR).
4790
+ * { contractId, decimals } NEAR · { metadata, decimals } Aptos ·
4791
+ * { assetId, decimals } Algorand).
4782
4792
  */
4783
4793
  declare class UnknownTokenError extends PipRailError {
4784
4794
  readonly code = "UNKNOWN_TOKEN";
@@ -4790,7 +4800,8 @@ declare class UnknownTokenError extends PipRailError {
4790
4800
  * `npm install @ton/ton @ton/core @ton/crypto`; Stellar:
4791
4801
  * `npm install @stellar/stellar-sdk`; XRPL: `npm install xrpl`; Tron:
4792
4802
  * `npm install tronweb`; Sui: `npm install @mysten/sui`; NEAR:
4793
- * `npm install near-api-js`.
4803
+ * `npm install near-api-js`; Aptos: `npm install @aptos-labs/ts-sdk`;
4804
+ * Algorand: `npm install algosdk`.
4794
4805
  */
4795
4806
  declare class MissingDriverError extends PipRailError {
4796
4807
  readonly code = "MISSING_DRIVER";
package/dist/index.d.ts CHANGED
@@ -3845,7 +3845,7 @@ interface AlgorandToken {
3845
3845
  }
3846
3846
  /**
3847
3847
  * What to be paid in. Each driver validates the forms it accepts:
3848
- * - 'native' the chain's native coin (ETH, BNB, SOL, TON, XLM, XRP)
3848
+ * - 'native' the chain's native coin (ETH, BNB, SOL, TON, XLM, XRP, TRX, NEAR, SUI, APT, ALGO)
3849
3849
  * - 'USDC' (string) a symbol resolved against the chosen chain
3850
3850
  * - EvmToken any ERC-20 (EVM chains)
3851
3851
  * - SolanaToken any SPL token (Solana)
@@ -4014,8 +4014,11 @@ interface PaymentPolicy {
4014
4014
  /** Allowlist of chains the agent may pay on. A 402 on any other chain is
4015
4015
  * declined. Strings match the configured selector; objects match by id. */
4016
4016
  chains?: ChainSelector[];
4017
- /** Allowlist of token symbols (matched against the TRUE symbol). An asset the
4018
- * SDK can't recognise never satisfies this list. */
4017
+ /** Allowlist of token symbols (matched against the TRUE symbol). The special
4018
+ * value `'native'` is a chain-agnostic alias for the chain's native coin — it
4019
+ * matches the native asset on ANY family (ETH/BNB/TRX/XLM/…) without naming
4020
+ * the ticker, mirroring the merchant-side `token: 'native'`. An asset the SDK
4021
+ * can't recognise never satisfies this list. */
4019
4022
  tokens?: string[];
4020
4023
  /** Allowlist of hosts. Exact (`api.example.com`) or wildcard (`*.example.com`). */
4021
4024
  hosts?: string[];
@@ -4130,6 +4133,8 @@ type PipRailEvent = {
4130
4133
  * - Stellar → `{ secret }` (S… seed) or a ready `{ keypair }`
4131
4134
  * - XRPL → `{ seed }` (s… seed) or a ready `{ wallet }`
4132
4135
  * - NEAR → `{ accountId, privateKey }` (privateKey = ed25519:… secret)
4136
+ * - Aptos → `{ privateKey }` (AIP-80 `ed25519-priv-0x…` / raw `0x…` hex) or `{ account }`
4137
+ * - Algorand→ `{ mnemonic }` (25 words) or a ready `{ account }`
4133
4138
  */
4134
4139
  type WalletInput = {
4135
4140
  privateKey: string;
@@ -4263,7 +4268,7 @@ interface PipRailClientOptions {
4263
4268
  /** Wallet for the chosen chain family. */
4264
4269
  wallet: WalletInput;
4265
4270
  /** Which chain to pay on. EVM ('bnb'|'base'|…), 'solana', 'ton', 'stellar',
4266
- * 'xrpl', 'tron', 'sui', or 'near'. */
4271
+ * 'xrpl', 'tron', 'sui', 'near', 'aptos', or 'algorand'. */
4267
4272
  chain: ChainSelector;
4268
4273
  /** Override the chain's default RPC URL (recommended in production). */
4269
4274
  rpcUrl?: string;
@@ -4441,8 +4446,10 @@ interface AgentTool {
4441
4446
  invoke: (args: Record<string, unknown>) => Promise<unknown>;
4442
4447
  }
4443
4448
  /**
4444
- * Two tools wrapping a configured {@link PipRailClient}:
4449
+ * Three tools wrapping a configured {@link PipRailClient}:
4445
4450
  * - `piprail_quote_payment(url)` — price a gated URL WITHOUT paying.
4451
+ * - `piprail_plan_payment(url)` — check you CAN pay (balance + gas + recipient
4452
+ * readiness) across every rail the URL offers, WITHOUT paying.
4446
4453
  * - `piprail_pay_request(url, method?, body?)` — pay if needed and return the result.
4447
4454
  *
4448
4455
  * A policy/approval refusal comes back as a structured `{ declined: true, reason }`
@@ -4478,7 +4485,7 @@ declare function paymentTools(client: PipRailClient): AgentTool[];
4478
4485
  */
4479
4486
  interface AcceptOption {
4480
4487
  /** Which chain. EVM ('bnb'|'base'|…), 'solana', 'ton', 'stellar', 'xrpl',
4481
- * 'tron', 'near', or 'sui'. */
4488
+ * 'tron', 'near', 'sui', 'aptos', or 'algorand'. */
4482
4489
  chain: ChainSelector;
4483
4490
  /** Token to be paid in (symbol / 'native' / custom descriptor). */
4484
4491
  token: TokenInput;
@@ -4492,8 +4499,9 @@ interface AcceptOption {
4492
4499
  interface RequirePaymentOptions {
4493
4500
  /**
4494
4501
  * Single-chain form: which chain to accept payment on. EVM ('bnb'|'base'|…),
4495
- * 'solana', 'ton', 'stellar', 'xrpl', 'tron', 'near', or 'sui'. Provide
4496
- * `chain` + `token` + `amount`, OR use the multi-chain `accept` array below.
4502
+ * 'solana', 'ton', 'stellar', 'xrpl', 'tron', 'near', 'sui', 'aptos', or
4503
+ * 'algorand'. Provide `chain` + `token` + `amount`, OR use the multi-chain
4504
+ * `accept` array below.
4497
4505
  */
4498
4506
  chain?: ChainSelector;
4499
4507
  /** Override the chain's default RPC URL (recommended in production). */
@@ -4504,9 +4512,10 @@ interface RequirePaymentOptions {
4504
4512
  * `{ address, decimals }` on EVM/Tron, `{ mint, decimals }` on Solana,
4505
4513
  * `{ master, decimals }` on TON, `{ issuer, code, decimals }` on Stellar,
4506
4514
  * `{ issuer, currencyHex, decimals }` on XRPL, `{ contractId, decimals }` on
4507
- * NEAR, `{ coinType, decimals }` on Sui. You name the token; the SDK fills in
4515
+ * NEAR, `{ coinType, decimals }` on Sui, `{ metadata, decimals }` on Aptos, or
4516
+ * `{ assetId, decimals }` on Algorand. You name the token; the SDK fills in
4508
4517
  * the contract + decimals for built-in symbols. (Note: native USDC doesn't
4509
- * exist on TON/Tron — USDT does; NEAR is FT-only, so `'native'` is rejected.)
4518
+ * exist on TON/Tron — USDT does; native NEAR is supported via `'native'`.)
4510
4519
  */
4511
4520
  token?: TokenInput;
4512
4521
  /** Human-readable amount, e.g. "0.05" (single-chain form). */
@@ -4518,8 +4527,8 @@ interface RequirePaymentOptions {
4518
4527
  */
4519
4528
  accept?: AcceptOption[];
4520
4529
  /** Address that receives the payment (0x… EVM/Sui, base58 Solana, EQ…/UQ… TON,
4521
- * G… Stellar, r… XRPL, T… Tron, account id on NEAR). Required for the single
4522
- * form; the per-option fallback for the multi form. */
4530
+ * G… Stellar, r… XRPL, T… Tron, account id on NEAR, 0x… Aptos, base32 Algorand).
4531
+ * Required for the single form; the per-option fallback for the multi form. */
4523
4532
  payTo?: AddressId;
4524
4533
  /** Shown to the agent in the challenge. */
4525
4534
  description?: string;
@@ -4669,7 +4678,7 @@ declare class InsufficientFundsError extends PipRailError {
4669
4678
  * The message states the requirement and the fix in plain language **and echoes
4670
4679
  * the raw chain code** (e.g. `(XRPL: tecNO_DST_INSUF_XRP)`), while the untouched
4671
4680
  * chain error is preserved on `.cause` for deeper debugging. Chains with no
4672
- * receive prerequisite (EVM, Solana, Sui, Tron, and native TON/NEAR) never throw it.
4681
+ * receive prerequisite (EVM, Solana, Sui, Aptos, Tron, and native TON/NEAR) never throw it.
4673
4682
  */
4674
4683
  declare class RecipientNotReadyError extends PipRailError {
4675
4684
  readonly code = "RECIPIENT_NOT_READY";
@@ -4766,7 +4775,7 @@ declare class NonReplayableBodyError extends PipRailError {
4766
4775
  }
4767
4776
  /**
4768
4777
  * The chosen chain belongs to one family (EVM, Solana, TON, Stellar, XRPL, Tron,
4769
- * Sui, NEAR) but the wallet, payTo, or token was given in another family's form
4778
+ * Sui, NEAR, Aptos, Algorand) but the wallet, payTo, or token was given in another family's form
4770
4779
  * (e.g. an `0x…` payTo on Solana, or a `{ mint }` token on a Stellar chain).
4771
4780
  */
4772
4781
  declare class WrongFamilyError extends PipRailError {
@@ -4778,7 +4787,8 @@ declare class WrongFamilyError extends PipRailError {
4778
4787
  * token by full descriptor ({ address, decimals } EVM/Tron · { mint, decimals }
4779
4788
  * Solana · { master, decimals } TON · { issuer, code, decimals } Stellar ·
4780
4789
  * { issuer, currencyHex, decimals } XRPL · { coinType, decimals } Sui ·
4781
- * { contractId, decimals } NEAR).
4790
+ * { contractId, decimals } NEAR · { metadata, decimals } Aptos ·
4791
+ * { assetId, decimals } Algorand).
4782
4792
  */
4783
4793
  declare class UnknownTokenError extends PipRailError {
4784
4794
  readonly code = "UNKNOWN_TOKEN";
@@ -4790,7 +4800,8 @@ declare class UnknownTokenError extends PipRailError {
4790
4800
  * `npm install @ton/ton @ton/core @ton/crypto`; Stellar:
4791
4801
  * `npm install @stellar/stellar-sdk`; XRPL: `npm install xrpl`; Tron:
4792
4802
  * `npm install tronweb`; Sui: `npm install @mysten/sui`; NEAR:
4793
- * `npm install near-api-js`.
4803
+ * `npm install near-api-js`; Aptos: `npm install @aptos-labs/ts-sdk`;
4804
+ * Algorand: `npm install algosdk`.
4794
4805
  */
4795
4806
  declare class MissingDriverError extends PipRailError {
4796
4807
  readonly code = "MISSING_DRIVER";
package/dist/index.js CHANGED
@@ -733,7 +733,7 @@ function makeEvmNetwork(resolved) {
733
733
  }
734
734
  return { token, native };
735
735
  },
736
- // EVM has no receive prerequisite — any 0x address receives native or ERC-20 immediately.
736
+ // No receive prerequisite — any 0x address receives native or ERC-20 immediately.
737
737
  async recipientReady() {
738
738
  return { ready: "n/a" };
739
739
  },
@@ -923,7 +923,12 @@ function evaluatePolicy(intent, policy, spentForAssetBase) {
923
923
  }
924
924
  if (policy.tokens) {
925
925
  const sym = intent.recognized ? intent.symbol : void 0;
926
- if (!sym || !policy.tokens.some((t) => t.toUpperCase() === sym.toUpperCase())) {
926
+ const isNative = intent.asset === "native";
927
+ const matches = policy.tokens.some((t) => {
928
+ if (isNative && t.toUpperCase() === "NATIVE") return true;
929
+ return sym ? t.toUpperCase() === sym.toUpperCase() : false;
930
+ });
931
+ if (!matches) {
927
932
  return deny(
928
933
  `token ${intent.symbol ?? intent.asset} is not in the allowed set (policy.tokens).`
929
934
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piprail/sdk",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Accept x402 crypto payments across 28 chains — every major EVM chain plus Solana, TON, Tron, NEAR, Sui, Aptos, Algorand, Stellar & XRPL — in a couple of lines. No backend, no database, no fee; payments settle straight to your wallet.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -82,13 +82,13 @@
82
82
  "peerDependencies": {
83
83
  "@aptos-labs/ts-sdk": ">=2 <8",
84
84
  "@mysten/sui": ">=2 <3",
85
- "algosdk": ">=3 <4",
86
85
  "@solana/spl-token": "^0.4.0",
87
86
  "@solana/web3.js": "^1.95.0",
88
87
  "@stellar/stellar-sdk": ">=13 <16",
89
88
  "@ton/core": ">=0.59 <1",
90
89
  "@ton/crypto": ">=3 <4",
91
90
  "@ton/ton": ">=15 <17",
91
+ "algosdk": ">=3 <4",
92
92
  "bs58": "^5.0.0",
93
93
  "near-api-js": ">=7 <8",
94
94
  "tronweb": ">=6 <7",