For agents
Your agent spends
with its own key
The owner writes the mandate. The agent only calls spend(). It cannot raise its caps, add a recipient or touch the owner's key.
Give it a key
Create a fresh key for each agent and fund it with a little ETH for gas. Put its address in the policy editor. Never give an agent your own wallet's key.
Call spend()
The client in examples/ wraps preview(), spend() and cancel(). The sample script runs one spend from your shell variables.
import { createOrqenAgent } from "./examples/orqen-agent";
const agent = createOrqenAgent({
rpcUrl: "https://rpc.mainnet.chain.robinhood.com",
mandates: "0x656839BE018b4632378DA2337A590F9C14c94Ca3",
privateKey: process.env.ORQEN_AGENT_KEY, // the agent's own key
});
// Free: what would happen?
const p = await agent.preview(1n, seller, 1_500_000n); // 1.5 USDG
// Spend. A wait or a refusal is an outcome, not an error.
const r = await agent.spend(1n, seller, 1_500_000n, "invoice-42");
if (r.outcome === "queued") console.log("waiting for the owner:", r.reason);Read the outcome
Every call ends in one recorded decision. Only a key that is not the mandate's agent makes the call revert.
| Outcome | When | Event |
|---|---|---|
| sent | listed recipient, within the cap per payment and the rolling 24-hour cap, mandate live | Spent |
| queued | new recipient, over a cap, or both (reasons as bit flags 1, 2, 4) | SpendQueued |
| refused | revoked, expired, zero, a forbidden recipient, a full queue, or the token refused | SpendRefused |
Machine-readable
The ABI, address and deploy block are at /contracts.json; a plain summary for language models is at /llms.txt. OrqenMandates is live.