npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ackrate/core

v0.3.3

Published

Thin, untrusted client for the Ackrate MandateRegistry. The contract is the source of truth.

Readme

@ackrate/core 0.3.1

Create an agent, connect to the live MandateRegistry contract on Stellar, and run a crash-safe mandate-validated payment through a small typed surface.

@ackrate/core is the high-level client for Ackrate, a protocol for agent-driven payments where the spending limit lives inside a Soroban smart contract instead of the application. A user signs a mandate that fixes a budget, a single payee, and an expiry. An agent spends against that mandate, and every payment is validated and consumed on-chain by the contract before any money moves.

The SDK is untrusted by design. It never custodies funds and it never enforces the limit. If the SDK has a bug, or the agent key is stolen, the contract still rejects anything outside the mandate: overspending, paying the wrong merchant, replaying a payment, or paying after the user revokes.

Install

npm install @ackrate/[email protected] @stellar/[email protected]

@stellar/stellar-sdk is a direct dependency you also import yourself for Keypair. The package ships its own ESM build with TypeScript types.

Quick start (Stellar testnet)

import { ackrate } from "@ackrate/core";
import { Keypair } from "@stellar/stellar-sdk";

const user = Keypair.fromSecret(USER_SECRET);   // owns the funds, signs the mandate
const agent = Keypair.fromSecret(AGENT_SECRET);  // the autonomous spender

const mandate = ackrate.createIntentMandate({
  user: user.publicKey(),
  agent: agent.publicKey(),
  merchant: MERCHANT_ADDRESS,
  asset: ackrate.testnet.nativeSac,        // native XLM as a SEP-41 token
  maxAmount: "5.00",                      // total budget the agent may spend
  expiry: Math.floor(Date.now() / 1000) + 3600,
});

await ackrate.registerMandate(mandate, { signer: user });  // store the mandate on-chain
await ackrate.approveBudget(mandate, { signer: user });     // SEP-41 allowance to the contract
const hash = await ackrate.agent({ mandate, signer: agent }).pay("1.00", {
  // Must durably save the signed hash before the SDK broadcasts it.
  onPrepared: (pending) => paymentJournal.save(pending),
});

After pay returns, one real payment has settled on testnet. hash is the transaction hash, which you can open on a Stellar explorer.

How it works

The flow has three signers and one contract. The user authorizes, the agent spends, and the contract is the gate every payment passes through.

  1. createIntentMandate builds the mandate object and its canonical id locally. No network call happens here. The id is a hash of the mandate fields and becomes the on-chain storage key.
  2. registerMandate writes the mandate to the contract, signed by the user. The contract sets spent to 0, seq to 0, and status to Active itself, so a caller cannot seed tampered state.
  3. approveBudget approves a SEP-41 allowance up to the budget. The allowance goes to the contract, never to the agent or the SDK. This is the custody boundary: the agent can ask the contract to move money, but only the contract holds the right to pull from the user.
  4. pay calls execute_payment, signed by the agent. The contract re-checks the agent, the sequence, the merchant scope, the expiry, and the remaining budget, then advances spent and seq and transfers the funds from user to merchant in one atomic step. If any check fails, the whole call reverts and pay throws.

Paying for a resource (bound-v2 x402)

agent.fetch(url) is the x402 client. For new paid endpoints, create the agent with proofPolicy: "bound-v2-only". It advertises the bound-v2 capability and refuses a legacy challenge before paying. The authenticated challenge fixes the merchant's exact public origin, GET method, path and query, network, registry, merchant, asset, amount, decimals, and validity window. After execute_payment settles, the agent signs that exact challenge together with the transaction hash and mandate id, then retries with the bound proof.

The contract still enforces the spending limit. A revoked, expired, over-budget, replayed, or out-of-scope payment is rejected on-chain; neither the SDK nor a cached mandate can bypass execute_payment.

import { getSettlementReceipt } from "@ackrate/core";

const agent = ackrate.agent({
  mandate,
  signer: agentKey,
  proofPolicy: "bound-v2-only",
  receiptStore, // required for paid fetch; durable SettlementReceiptStore
});
const res = await agent.fetch("https://merchant.example/report");
const data = await res.json(); // served only after the merchant verified the on-chain payment

const receipt = getSettlementReceipt(res); // exact proof for gate checking/recovery
await persistAcceptedResult(data, receipt); // application-owned durable commit
await agent.acknowledgeDelivery(receipt!);   // only now clear the payment lock

Before broadcast, the agent signs the transaction, derives its canonical hash and validity deadline, and makes the exact receipt durable. If that storage write fails, fetch aborts before broadcast and propagates the storage error. Once the receipt is durable, any uncertain broadcast/final ledger result, paid-retry network failure, non-2xx status, or incomplete body throws DeliveryPendingError with that SettlementReceipt. Do not call fetch again, because a fresh 402 could create another payment. Reconcile and retry the exact existing proof:

import { DeliveryPendingError } from "@ackrate/core";

try {
  await agent.fetch("https://merchant.example/report");
} catch (error) {
  if (error instanceof DeliveryPendingError) {
    console.log("prepared payment transaction", error.receipt.txHash);
    const response = await agent.retryDelivery(error.receipt);
    const result = await response.json();
    await persistAcceptedResult(result, error.receipt);
    await agent.acknowledgeDelivery(error.receipt);
    // No payment or signature occurs during retryDelivery.
  } else {
    throw error;
  }
}

retryDelivery verifies the receipt id, mandate, proof version, exact signed origin, method, path, and query. It never pays or signs and always disables redirects so proof material cannot be forwarded to another origin. A retry is not ready for acknowledgment until the complete successful response body has been received. The receipt remains durable and blocks another payment until the application validates/persists its business result and explicitly calls acknowledgeDelivery. Treat every receipt as sensitive bearer data for its exact request. A production merchant also needs one durable, linearizable settlement claim and immutable-result store keyed by the settlement so a lost response can replay the same bytes without charging or running fulfillment again.

The x402 wire format lives in its own module, so it tracks the evolving x402 spec without touching the mandate or the contract. Use @ackrate/express-middleware to build an Express 4/5 merchant that independently verifies the on-chain settlement before serving.

API

ackrate.createIntentMandate(input, net?)

Builds an AP2-style mandate and its on-chain id locally, with no chain call. The default nonce makes each id unique; pass an explicit nonce for a deterministic id.

| Field | Type | Meaning | |---|---|---| | user | string | Stellar address that owns the funds and signs the mandate | | agent | string | The only address allowed to call execute_payment | | merchant | string | The single payee this mandate is scoped to | | asset | string | SEP-41 / SAC contract id of the token (use ackrate.testnet.nativeSac for XLM) | | maxAmount | string | Total budget as a decimal string, e.g. "5.00" | | expiry | number | Unix seconds after which the mandate is dead | | decimals | number? | Token decimals, default 7 (Stellar assets) | | nonce | string? | Optional explicit nonce; defaults to a unique value so ids do not collide |

Returns an IntentMandate with the hex id, the raw idBuffer, the parsed fields, and maxAmount as a bigint in stroops.

ackrate.registerMandate(mandate, { signer }, net?)

Stores the mandate on-chain. Signed by the user. Returns the transaction hash.

ackrate.approveBudget(mandate, { signer }, net?)

Approves the contract for a SEP-41 allowance up to the mandate budget. Signed by the user. Returns the transaction hash.

ackrate.agent({ mandate, signer }, net?).pay(amount, lifecycle)

Reads the current mandate sequence, then calls execute_payment for amount (a decimal string), signed by the agent. Returns the transaction hash. Throws if the contract rejects the payment.

Every direct pay call must pass a PaymentSubmissionLifecycle. Its async onPrepared hook receives the signed hash, sequence, and exact validity deadline before any broadcast; persist that record atomically or throw to abort without sending. Ackrate's CLI uses this hook and refuses another payment until settlement reconcile proves the result and an exact successful hash is explicitly acknowledged.

For a user-visible operation that may be retried after a lost HTTP response, also pass its immutable expectedSeq. The SDK compares it with current contract state before signing. If a prior attempt already consumed that sequence, retry fails before another transaction is created; the contract repeats the same check at execution. Concurrent same-mandate operations in one process are rejected by a synchronous claim before the first chain read.

ackrate.agent({ mandate, signer, proofPolicy?, receiptStore? }, net?)

Creates an agent. Set proofPolicy to "bound-v2-only" for every new paid endpoint. The default "legacy-compatible" exists only for migrations. receiptStore implements SettlementReceiptStore and is required before any 402-triggered payment. savePending must become durable before transaction broadcast, listPending lets a new process restore the no-second-payment lock, and clearPending records explicit application acknowledgment after full-body success.

agent.fetch(url, init?)

The x402 client. It requests url with bound-v2 capability negotiation; on a valid 402 it checks the request and mandate binding, signs the transaction, persists its hash plus bound proof before broadcast, pays on-chain through the same pay path, and retries with X-PAYMENT. Automatic redirects are disabled before and after settlement. A non-402 response is returned unchanged, with no payment.

If the paid retry fails to connect, returns a non-2xx status, or fails before the full successful response body is received after settlement, throws DeliveryPendingError carrying a SettlementReceipt with the transaction hash and exact proof. A submitted-but-unconfirmed transaction also produces the same recoverable receipt and blocks every new payment on that agent until it is reconciled.

agent.retryDelivery(receipt, init?)

Retries HTTP delivery with the receipt's existing X-PAYMENT proof. It never calls pay, never signs, and never submits a transaction. It rejects a receipt belonging to a different mandate or exact signed request. With a configured receipt store, successful full-body delivery remains pending until the application acknowledges it.

agent.acknowledgeDelivery(receipt)

Validates the exact receipt and removes it from durable pending state. Call this only after the complete HTTP body has been validated and the business result is durably accepted. If acknowledgment storage fails, it throws DeliveryPendingError and keeps new payments blocked. This explicit boundary prevents a crash between transport success and application commit from silently creating a second purchase.

agent.getPendingSettlement() and agent.reconcilePendingSettlement()

getPendingSettlement returns the captured hash, sequence, and validity deadline for a prepared transaction whose broadcast/final result or paid delivery has not been closed. While it is present, pay and fetch fail closed instead of risking a second payment. On restart, the first operation hydrates the same lock from receiptStore.listPending. After restarting a direct-pay process, pass the exact durable journal record to reconcilePendingSettlement(record); it validates the mandate and queries that hash without submitting anything. The result is pending, failed, expired, or succeeded. A succeeded settlement with a receipt remains locked until recovery and explicit application acknowledgment finish the original delivery.

getSettlementReceipt(response)

Returns the immutable receipt attached to a successful paid response. It includes receiptId, proof version, exact URL and method, transaction hash, mandate id, amount, and the full settlement proof.

DeliveryPendingError, SettlementUncertainError, SettlementReceipt, and SettlementReceiptStore

Typed post-submission recovery evidence. The error means a canonical transaction hash exists and starting another payment is unsafe until that same hash is reconciled and its delivery is closed. Surface the hash to the user and retry the same receipt; never start another payment automatically. A receipt store must protect the full proof as sensitive data and provide atomic durable savePending, listPending, and clearPending operations. Multi-process consumers also need shared linearizable storage rather than the reference file store.

SettlementUncertainError is the direct-pay equivalent: broadcast was attempted and the transaction may have been submitted, but the SDK did not prove a final ledger result. Retain its transaction hash and call reconcilePendingSettlement on the same agent before attempting any other spend.

ackrate.revokeMandate(mandate, { signer }, net?)

Marks the mandate revoked. Signed by the user. After this, every pay is rejected on-chain.

toStroops(human, decimals?)

Converts a decimal string to stroops as a bigint. Strict by design, because this is money: only a non-negative decimal such as "5" or "5.00" is accepted. Negatives, scientific notation, garbage, or more fraction digits than decimals all throw rather than produce a wrong on-chain value.

Errors

Typed contract error codes, re-exported so you can branch on a rejection.

The signer field on every user or agent call accepts either a Keypair or a raw secret string.

Amounts

Amounts are decimal strings, not floats. "5.00", "0.01", and "100" are valid. The SDK converts them to integer stroops with the asset's decimals (7 by default) and rejects anything ambiguous, so you never round money by accident.

Errors and what the contract refuses

When pay (or any call) is rejected on-chain, the SDK throws and the reason maps to a typed code. These are the guarantees a compromised agent or SDK cannot get around:

| Code | Name | Cause | |---|---|---| | Errors[1] | AlreadyExists | A mandate with that id is already registered | | Errors[2] | NotFound | No mandate with that id | | Errors[4] | MandateExpired | The payment happened at or after expiry | | Errors[5] | MandateRevoked | The user revoked the mandate | | Errors[6] | BudgetExceeded | The spend would push spent past maxAmount | | Errors[7] | MerchantOutOfScope | The payee is not the mandate's merchant | | Errors[8] | BadSequence | A replayed or out-of-order payment | | Errors[9] | InvalidAmount | A non-positive amount | | Errors[10] | Paused | The contract's money path is paused | | Errors[11] | UpgradeNotScheduled | No upgrade is pending | | Errors[12] | UpgradeNotReady | The one-hour delay has not elapsed | | Errors[13] | UpgradeAlreadyScheduled | An upgrade is already pending | | Errors[14] | UpgradeRequiresPause | Execution requires paused state |

try {
  await ackrate.agent({ mandate, signer: agent }).pay("100.00", {
    onPrepared: (pending) => paymentJournal.save(pending),
  });
} catch (err) {
  // The contract refused: budget, scope, expiry, replay, or revocation.
  // Inspect the thrown message, or compare against Errors[...] codes.
}

Network

@ackrate/core defaults to Stellar testnet and the upgradeable simple MandateRegistry pinned in @ackrate/stellar: CCHQ5G4Y4YBMY6D3TYYJSVJVCKUM22Q6TMKCCHVAHY4X7K6QELQACZRM. Pass a custom NetworkConfig as the last argument to any call to select a different compatible deployment, RPC, or passphrase.

The current contract WASM SHA-256 is ba370a80369daa0a0dea2554410dca6f2a9f7a76ba707cb92a83434e2fe76e87, matching the reproducible simple-v0.2.3 release.

ackrate.testnet            // the default NetworkConfig
ackrate.testnet.nativeSac  // native XLM as a SEP-41 contract id
ackrate.testnet.mandateRegistryId // the live contract id

Relationship to @ackrate/stellar

@ackrate/core is built on @ackrate/stellar, which holds the typed MandateRegistry bindings, network config, signing adapter, and SEP-41 helpers. Use core for the agent and payment flow. Drop down to @ackrate/stellar only when you need direct, typed access to the contract.

License

Apache-2.0.