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

@arcurrent/mandate-sdk

v0.1.0

Published

Typed client for MandateEscrow, the open, permissionless escrow and reputation primitive live on Arc mainnet.

Readme

@arcurrent/mandate-sdk

A typed client for MandateEscrow, the open, permissionless escrow and reputation contract live on Arc mainnet at 0xca901f58fb82FE5FF459264a419b8cF8c75b3371 (source verified).

Anyone can fund a mandate for anyone (or leave it open), a fulfiller posts a hash of their proof, and the funder releases the USDC atomically, optionally split across several addresses in one transaction. Every outcome updates an on-chain reputation ledger per address. There is no owner and no fee.

You do not need to be an "Arc project" to use it: any script or agent can call it as long as the transaction is sent to Arc mainnet (a wallet with a little USDC for gas).

Status: lives in this monorepo and is not published to npm yet. Until it is, build it from the repo (npm install && npm run build -w @arcurrent/mandate-sdk) and depend on the workspace.

Quickstart

import { MandateClient } from "@arcurrent/mandate-sdk";

// Read-only: no wallet, no key.
const reader = new MandateClient();
const mandate = await reader.getMandate(4);        // null if the id does not exist
const rep = await reader.reputationOf("0xFde19f3BCd5482544ce672391d839a56dA96BFEE");

// Read-write: use a dedicated, low-balance wallet for anything autonomous.
const client = MandateClient.fromPrivateKey(process.env.PRIVATE_KEY as `0x${string}`, {
  maxAmountUsdc: "25", // hard ceiling per createMandate
});

// 1. Lock USDC for a fulfiller (omit `fulfiller` to leave it open, omit `deadline` for no refund path).
const { mandateId } = await client.createMandate({
  amountUsdc: "10",
  fulfiller: "0xFulfillerAddress",
  deadline: { days: 14 },
});

// 2. The fulfiller posts proof (as text, which is hashed, or a ready-made 32-byte hash).
await fulfillerClient.submitProof(mandateId, { text: "https://example.com/delivered-work" });

// 3. The funder releases: whole amount to the fulfiller, or an atomic split.
await client.release(mandateId);
await client.release(mandateId, {
  splits: [
    { to: "0xFulfillerAddress", amountUsdc: "9.5" },
    { to: "0xFeeAddress", amountUsdc: "0.5" },
  ],
});

// Or, if nobody delivered before the deadline:
await client.refund(mandateId);

API

| Method | Needs wallet | What it does | |---|---|---| | getMandate(id) | no | One mandate, or null. | | listMandates({ limit, offset }) | no | Newest first. | | reputationOf(address) | no | { completed, refunded, volumeSettled } from the contract's ledger. | | verifyProof(id, evidenceText) | no | Does this text hash to the proof committed on-chain? | | createMandate({ amountUsdc, fulfiller?, deadline? }) | yes | Approves the exact amount if needed, then locks it. Returns the new id, read from this transaction's own event. | | submitProof(id, { text } \| { hash }) | yes | As the designated fulfiller, or first caller on an open mandate. | | release(id, { splits? }) | yes | Funder only. Splits must sum exactly to the locked amount. | | refund(id) | yes | After the deadline, if no proof was posted. |

Helpers: parseUsdc, formatUsdc, hashProof, resolveSplits, plus the raw mandateEscrowAbi, ARC_MAINNET and arcMainnetChain for wiring into other viem code.

AgentVault: pay from a treasury that bounds you

VaultClient talks to an AgentVault, a contract that holds a treasury and lets an agent (the operator) pay only inside rules a human (the owner) set on-chain: a per-payment cap, a daily cap that refills continuously, an optional payee allowlist, and a pause switch. The operator can never withdraw or change a rule, so leaked agent credentials are bounded by numbers the owner chose, not by the balance.

import { VaultClient } from "@arcurrent/mandate-sdk";

// The agent: an operator key that holds only gas.
const agent = VaultClient.fromPrivateKey(process.env.OPERATOR_KEY as `0x${string}`, {
  vaultAddress: "0xYourVault",
});

const policy = await agent.getPolicy();   // balance, caps, allowance available right now, paused, roles

// Ask first: never throws, spends no gas, and says exactly why a payment would be refused.
const check = await agent.checkPay({ to: "0xVendor", amountUsdc: "0.5" });
if (!check.ok) console.log(check.code, check.message); // e.g. OVER_DAILY_ALLOWANCE: refills in ~40 minutes

// One atomic transaction: creates and releases a MandateEscrow mandate.
const { mandateId, explorerUrl } = await agent.pay({ to: "0xVendor", amountUsdc: "0.5", ref: "invoice-42" });

// The owner (a different wallet): rules, funds, emergency stop.
const owner = VaultClient.fromPrivateKey(process.env.OWNER_KEY as `0x${string}`, { vaultAddress: "0xYourVault" });
await owner.setLimits({ perPaymentUsdc: "2", dailyUsdc: "10" });
await owner.setPayee("0xVendor", true);
await owner.pause();          // the owner or the guardian; only the owner resumes
await owner.withdraw("0xOwner", "5"); // works even while paused

| Method | Who | What it does | |---|---|---| | getPolicy(), isPayeeAllowed(), getWiring() | anyone | Live rules, balance and the escrow and token the vault is wired to. | | checkPay({ to, amountUsdc }) | anyone | Would this payment go through right now, and if not, why. | | pay({ to, amountUsdc, ref? }) | operator | Pays through the vault; refused up front with a specific code if any rule would reject it. | | setLimits, setAllowlistRequired, setPayee(s), setOperator, setGuardian, unpause, withdraw, transferOwnership | owner | Change rules, rotate wallets, take funds out. | | pause() | owner or guardian | Stop all payments. | | acceptOwnership() | pending owner | Second step of a handover. |

Extra error codes for the vault: PAUSED, OVER_PER_PAYMENT_CAP, OVER_DAILY_ALLOWANCE, PAYEE_NOT_ALLOWED, INVALID_PAYEE. Pass chain: arcTestnetChain (and your own escrow and vault addresses) to work against Arc testnet.

Built for callers that cannot afford surprises

  • Spend cap. maxAmountUsdc rejects any single createMandate above it before any read or transaction.
  • Exact-amount approvals. The escrow is approved for exactly what a mandate needs, not unlimited, unless you pass approveMax: true.
  • Simulate first. Every write is simulated, so a call the contract would reject (wrong caller, wrong status, deadline not reached) fails with a readable error and costs no gas.
  • Strict amounts. "1e3", negative numbers, and more than 6 decimals are rejected, never rounded.
  • Race-proof ids. The contract is permissionless, so guessing the id from nextMandateId() could return someone else's mandate. The id comes from the MandateCreated event in your own receipt, matched on your address.
  • Stable error codes. Failures are MandateError with a code (AMOUNT_OVER_CAP, NOT_AUTHORIZED, WRONG_STATUS, DEADLINE_NOT_REACHED, INSUFFICIENT_BALANCE, CONTRACT_REVERT, ...) you can branch on.
  • Receipt polling that works on Arc. Arc's public RPC can serve a block-filter poll from a backend that has not indexed a fresh transaction yet, so waitForTransactionReceipt can hang on a transaction that already confirmed. The SDK polls getTransactionReceipt directly against one endpoint instead.

Trust model, stated plainly

The funder's release is the only condition. There is no oracle judging the work and no arbitration: a funder can withhold release after a fulfiller has posted proof, and the contract offers no recourse for that. Reputation records outcomes, it does not prevent bad behaviour. The contract is self-reviewed (static analysis plus unit tests) but not professionally audited; keep amounts proportionate.