Mandate
Dev tools

Build on Mandate

Mandate is a standalone NEAR contract, not a feature baked into one app. Hire a Swarm gates every job's budget through the exact same two calls below — your app can too, instead of writing its own spend-limit logic from scratch.

mandate — docs — zsh
$cat quickstart.env

Quickstart

Contract IDs for this live testnet deployment.

env
NEXT_PUBLIC_NEAR_NETWORK=testnet
NEXT_PUBLIC_MANDATE_CONTRACT_ID=mandate-f70187.testnet
NEXT_PUBLIC_TASK_MARKET_CONTRACT_ID=market-f70187.testnet
$cat pattern.md

The pattern

Mandate never calls into your contract, and your contract never has to know Mandate exists at the type level — the caller just does both calls in sequence. If the first one panics, the second never runs.

pattern
1. authorize_spend(mandate_id, your_contract_id, amount) // fails closed
2. your_contract.your_method(...) // only reached if 1 succeeded
$cat integrate.ts

TypeScript — near-api-js

The exact pattern agents/src/coordinator.ts uses.

typescript
await account.functionCall({
contractId: MANDATE_CONTRACT_ID,
methodName: "authorize_spend",
args: { id: mandateId, target: yourContractId, amount: amountYocto },
gas: 30_000_000_000_000n,
});
// only reached if authorize_spend didn't throw
await account.functionCall({
contractId: yourContractId,
methodName: "yourMethod",
args: { /* ... */ },
gas: 30_000_000_000_000n,
attachedDeposit: BigInt(amountYocto),
});
$cat scratch.sh

near-cli

For scripting, testing, or a quick manual check against a deployed mandate.

bash
$near call $MANDATE_CONTRACT_ID authorize_spend \
$ '{"id": 0, "target": "'$YOUR_CONTRACT_ID'", "amount": "1000000000000000000000000"}' \
$ --accountId $AGENT_ACCOUNT
$ 
$near view $MANDATE_CONTRACT_ID get_mandate '{"id": 0}'

Mandate contract reference

issue_mandate
change
issue_mandate(owner, daily_cap, allowed_contracts, expires_at) → MandateId

Grant an agent a capped, allow-listed, optionally expiring permission. Called by whoever is funding the budget — a human, or a coordinator delegating a slice of its own mandate.

authorize_spend
change
authorize_spend(id, target, amount) → bool

Call this before your own contract call. Panics — aborting the whole transaction — if the mandate is revoked, expired, over its daily cap, or `target` isn't allow-listed. Succeeds silently otherwise.

revoke
change
revoke(id)

Kill a mandate immediately. Callable by its issuer, or by whatever account is currently registered as `guard` — independent of the issuer, for exactly this reason. Exercised for real on the live deployment: see the README.

set_guard
change
set_guard(new_guard)

Rotate the guard account. Callable only by the registry's `owner` — a third role, separate from `guard` and from any mandate's `issuer`, so losing the guard key doesn't mean losing the ability to replace it. Exercised live: rotated, the old key's `revoke` call was confirmed to fail, then rotated back.

get_mandate
view
get_mandate(id) → Mandate | null

Read one mandate's current state: cap, spend-so-far, allow-list, expiry, revoked flag.

list_mandates
view
list_mandates(from_index, limit) → Mandate[]

Page through every mandate ever issued. What a watcher process (or a dashboard) polls instead of needing an indexer.

get_guard / get_owner
view
get_guard() → AccountId · get_owner() → AccountId

Read the current guard and owner accounts.

Who else is already building on it

contracts/task-market (Hire a Swarm) gates every job's budget through Mandate using only the pattern above — it never imports or calls into Mandate beyond that one authorize_spend call. That decoupling is the point: your app can be the second.