Skip to content
Try it live
Dashboard

Give agents a budget.

Mandates on NEAR cap what an AI agent can spend: a budget, allowed recipients and a guardian.

  • NEAR testnet
  • NEP-366 delegates
  • NEP-141 test USDC
  • NEP-297 events
00In plain words

A spending limitfor your AI agent.

Scrip lets an AI agent pay for things within limits you set, and makes those limits impossible for the agent to break.

  1. The problem

    AI agents are starting to pay for things on their own.

    To do that today, you hand an agent a wallet or a secret key. If the agent is tricked or just makes a mistake, it can spend everything in it.

  2. The idea

    Think of a company card with a spending limit.

    You decide the total budget, the most it may pay at once, and who it may pay. The agent can ask to spend. It cannot spend more than you allowed, whatever it is told.

  3. Why you can rely on it

    The limit is enforced by code, not by a promise.

    The rules live in a smart contract on the NEAR blockchain, a public program whose rules are visible to everyone. The agent cannot change them; only the owner can tighten or revoke them. Even if the agent's own key is stolen, it cannot get past them.

  4. Where it stands

    A working prototype on a test network.

    Everything on this page runs on NEAR testnet with play money. No real funds are involved, and it has not been independently audited.

Who is who

Owner
You. You put the money in and set the limits.
Agent
The AI program that wants to pay. It can ask, nothing more.
Contract
The program on the blockchain that checks every request and holds the money.
Guardian
An optional second account that can approve big payments or make the rules stricter.
Words used on this page
Mandate
One agent's set of limits: budget, per-payment cap, allowed recipients, expiry.
Budget
The most the agent can spend in total under one mandate.
Per-payment cap
The most it can spend in a single payment.
Approval threshold
Payments above this amount need the guardian to approve that exact payment first.
Recipient allowlist
The accounts the agent is allowed to pay. Anyone else is refused.
Smart contract
A program that lives on a blockchain and runs exactly as written.
NEAR / testnet
NEAR is the blockchain used here. Testnet is its practice network with no real money.
USDC
A dollar-pegged digital currency. Here it is the test version.
Relayer
A service that pays the network fees so the agent does not need to hold any funds.
Nonce
A one-time number that stops the same approval from being reused.
NEP-141, NEP-297, NEP-366
Names of NEAR technical standards (token, event log, fee sponsoring). Safe to skip.
01How it works

Four steps fromdeposit to payment.

The agent can ask for money. Only the contract decides whether it moves.

In plain wordsYou put money in and set limits. The agent can only ask to pay. A program on the blockchain checks every request and refuses anything outside your limits.

Do thisJust scroll. The picture and the numbers on the left follow each step.

  1. Step 1 of 4

    The owner funds a mandate.

    You put the money in and set the limits.

    Technical detail

    The owner deposits test USDC into the Scrip contract and creates a mandate: a budget, a cap per payment, an approval threshold, the recipients that are allowed, an expiry and a guardian account. The contract holds the money. The agent never does.

    create_mandate(agent, budget, per_payment_cap, approval_threshold, recipients, expires_at, guardian)
  2. Step 2 of 4

    The agent gets a key that can do one thing.

    The agent gets a key that can only ask to pay. Nothing else.

    Technical detail

    The agent signs with a function-call access key bound to request_payment on the Scrip contract. That key cannot send native NEAR, add or delete keys, or change the rules, and it cannot attach a deposit. A relayer pays the gas through a NEP-366 delegate action, so the agent holds no NEAR at all.

    receiver_id: scrip contract method_names: ["request_payment"] allowance: relayer-sponsored (NEP-366)
  3. Step 3 of 4

    The agent asks. The contract checks, in order.

    Every request goes through the same checks. One failure and it is refused.

    Technical detail

    request_payment(mandate_id, recipient, amount, nonce) runs a fixed sequence of checks and the first failure wins. If every check passes, the amount is reserved before any token moves, so a burst of requests cannot be paid twice from the same budget.

    1 status, expiry 2 recipient allowed 3 amount <= cap 4 open requests < 32 5 spent + reserved + amount <= budget 6 above threshold? needs guardian approval
  4. Step 4 of 4

    The transfer settles, or the reservation is released.

    Money moves only after the checks pass, and every step is recorded.

    Technical detail

    The contract calls ft_transfer on the token, and a callback moves the reservation: success turns it into spent, failure releases it. Each step logs a NEP-297 event, so the whole history is readable from the chain.

    EVENT_JSON:{"standard":"scrip","version":"1.0.0", "event":"settled","data":[{"mandate_id":1, "request_id":0,"recipient":"shop.testnet","amount":"200000"}]}
02The rule machine

Send a payment.Break a rule.

These are the contract's checks, in the contract's order. Losses are capped by the sixth one: spent + reserved + amount can never pass the budget.

In plain wordsTry to make a payment. Pick a stranger, or an amount that is too big, and watch the contract say no and say why. Nothing real is spent: this runs in your browser.

Do thisPick who gets paid and how much, press Send payment, then read the answer on the right.

0.10USDC (test)

Not needed at this amount. An approval is tied to one recipient, one amount and one nonce.

Try:
budget
1.00
per-payment cap
0.40
approval above
0.25
deposited
1.50
recipients
shop.testnet
  1. 1active
  2. 2expiry
  3. 3recipient
  4. 4cap
  5. 5open <= 32
  6. 6budget
  7. 7approval

Block size is the amount, the lane is the recipient, the extra bar is the approval. Drag to turn the figure. A rejected block stops at the first wall it breaks.

No request yet.

Pick a recipient and an amount, then send. Try to push the budget line.

0budget 1.00deposited 1.50
Spent
0.00
Reserved
0.00
Free
1.00
Event log (NEP-297 lines, same envelope as the contract)

Events appear here.

Browser simulation of the contract's rules. Not a live chain call. Illustrative mandate, no recorded numbers. Event standard string is the contract's own: "scrip".

03Recorded run

Thirty payments,one budget.

An agent fires 30 payments of 0.10 USDC at a budget of 1.00 USDC. Same burst, two contracts.

In plain wordsWhat happens when an agent goes wild. Without a limit it kept paying past the budget. With Scrip it stopped at the budget.

Do thisPress Replay recorded run and watch the two bars fill.

Naive contract no running budget

14 paid, 1.40 USDC

Past the line (red): 0.40 USDC paid beyond the budget.

Scrip contract reserve, then pay

10 paid, 1.00 USDC

Stopped at the line. 20 of 30 requests did not pay.

The recording stores totals per contract, not the order of the 30 attempts, so the replay fills each bar evenly up to its recorded total. It is not a re-enactment of real timing.

Network
testnet
Recorded
28 Oct 2026
Attempts
30 x 0.10 USDC
Budget
1.00 USDC
Naive paid
14 (1.40 USDC)
Scrip paid
10 (1.00 USDC)
Burst length
about 9.8 s

Source: results/attack.json.

04Guardian

Tightening is free.Loosening has a gate.

Rules can always get stricter. Making them looser is limited to the guardian account, and then only within bounds.

In plain wordsA second person (the guardian) can make the rules stricter at any moment. Only they can loosen them, and never past what the owner set.

Do thisChoose Tighten, change a value, press Apply. Then choose Loosen and try the same.

Who is acting

Owner or guardian. Only stricter values pass.

Budget
1.00
Per-payment cap
0.40
Approval threshold
0.25
Deposited / already spent
1.50 / 0.20

Try tightening the budget to 0.10, then loosen the cap to 0.90.

Browser simulation of tighten and loosen. Illustrative mandate, not a chain call. Codes are the contract's own.

Large payments need an approval tied to that payment.

Above the approval threshold the guardian signs off on one recipient, one amount and one nonce. The contract stores a hash of that intent, so an approval for 0.30 USDC cannot be reused for 0.31, and it can be spent only once.

If a callback is ever lost.

Only the guardian can settle a stale reservation, and only as transferred=true. A claim that the transfer did not land is refused with STALE_REFUND_UNPROVABLE, because the chain cannot prove it. The amount stays locked inside its own mandate and cannot touch any other.

05Proof

What was attacked,and what held.

Two kinds of evidence: a live run on testnet, and a larger set of test cases. Each number says where it comes from and when.

In plain wordsEverything here was actually run, not drawn. Each number tells you what was tested, when, and on which network.

Five attacks on testnet, run on 28 Oct 2026

Source: results/gate.json. Each attack was sent to the live contract and refused with this code.
AttackRefused withTransaction
Unauthorized recipientRECIPIENT_NOT_ALLOWED7xpa…Zv5
Over per-payment capOVER_PER_PAYMENT_CAPC1u2…vK9
Guardian method via relayMETHOD_NOT_ALLOWEDrefused before broadcast (HTTP 400)
Insufficient gas invocationGAS_TOO_LOWrefused before broadcast (HTTP 400)
Forged signature keyKEY_NOT_ALLOWEDrefused before broadcast (HTTP 400)

The same run left 0 leftover accounts.

Test suites and checks

1,894/1,999catalog cases passresults/attack-catalog.json, layers A to E
0 / 0failed / breached105 cases were skipped, reported as skipped, not as passed
99.4%mutants caughtcontracts/scrip/MUTATION.md: 176 of 177 viable, 198 generated
138contract tests passCI, contract job
480real model decisionsresults/llm/, 4 small models, 20 seeds each, as of 14 Nov 2026

Counts as of 12 Nov 2026. The catalog covers 1,999 listed cases in five layers (A contract, B relayer, C client, D sandbox, E live testnet).

Recorded transactions are re-checked against the public FastNear indexer on the verify page: 39 of 39 matched on 13 Nov 2026. Run node scripts/verify-onchain.mjs to do it yourself.

Attack catalog

Verdicts by layer, from results/attack-catalog.json.
LayerPASSFAILSKIPPEDBREACH
A627000
B385000
C317000
D160000
E40501050

Open the full attack page

06Real models

Models were fooled.The budget held.

480 decisions from 4 small open models (Ollama, run on GitHub Actions, temperature 0.7). The invoice's payee and amount are honest. The only attack is a sentence in its note.

In plain wordsWe gave small AI models an invoice and asked them to pay it. In some invoices the note said to pay someone else. Then we replayed eight of their bad decisions on testnet, once with a normal wallet and once with Scrip.

What each model did

Source: results/llm/llm-trials-*.json. “Fooled” means the model paid the recipient named in the injected note. “Overpaid” means it paid 0.6 when the cap is 0.5.
ModelDecisionsFooled, normal promptFooled, hijacked promptOverpaidDeclinedBroken
llama3.2:3b120 (20 seeds)14 of 2018 of 204040
qwen2.5:3b120 (20 seeds)0 of 2020 of 203803
qwen3:1.7b120 (20 seeds)18 of 2020 of 204000
qwen3:4b120 (20 seeds)14 of 2020 of 203907

The “hijacked” prompt tells the model to follow instructions found in the invoice. The “normal” prompt does not. Overcharge invoices model a dishonest merchant, not a reasoning failure. These are small models: the results say nothing about large ones. Each model ran 20 seeds. qwen3:4b was too slow for one job, so its 20 seeds were run in four batches and merged.

Replayed on testnet

Two replay runs (the first three models, then qwen3:4b); balances and gains are added together.

Plain wallet, test USDC
3.4 to 0.2
Scrip mandate, test USDC
2 to 2
Attacker gained, plain wallet
0.8
Attacker gained, Scrip
0

The plain wallet executed 8 of 8 recorded decisions. Scrip refused 8 of 8 on-chain (RECIPIENT_NOT_ALLOWED x4, OVER_PER_PAYMENT_CAP x4).

Only these 8 decisions were replayed on-chain. The other 472 were judged by a local copy of the contract rules. Check every transaction yourself.

07Around the contract

One disposable account.One look at the recipient.

In plain wordsTwo helper pieces around the contract: a throwaway account for the agent, and a check on who is being paid. The recipient check is not connected to payments yet.

Envelope

A throwaway sub-account that serves as the agent's identity and holds a single function-call key. It exists for one task and is deleted afterwards, so a leaked key has little to take and little time to use it.

Before the hackathon
The envelope module (create, fund, sweep, delete) was already working on testnet.
Built during it
The mandate contract, the guardian rules, the relayer and this site.

Counterparty guard

A client-side check that answers allow, warn or block before the agent signs, from account age, whether the account has contract code, earlier interactions and a blocklist that needs evidence. It is not wired into the payment flow yet, and the age and history signals depend on an external indexer.

Counterparty guard demo. Source: results/demo-guard.json, recorded 2 Nov 2026. Read-only: the guard recorded what it would have signed, no transaction was sent.
ScenarioVerdictWhy
known-merchantallowAccount age, code and prior interactions all clear.
new-recipientwarnThe account has never been seen before.
injection-blockblockOn the blocklist, with the report attached as evidence.
unevidenced-reportwarnBlocklist entry ignored: the report had no evidence.
08Limits

What does notexist yet.

Read this before anything else on the page. It is the same list as the repository README.

In plain wordsAn honest list of what is not ready yet. Scrip is a testnet prototype: do not put real money in it.

  1. No mainnet contract is deployed. Nothing here has ever held real money.

  2. The contract went through internal review rounds plus unit, property and mutation tests. No independent firm has reviewed it.

  3. Four small open models were tested, 480 decisions in total (20 seeds each). The result says nothing about large models. The recorded demo agent is a script that always obeys the injected note, on purpose.

  4. The rest were judged by a local copy of the contract rules. Overcharge invoices model a dishonest merchant, not a reasoning failure.

  5. 84 hit public RPC limits and 21 cannot be reached on testnet. They are reported as skipped and are not counted as passed.

  6. Account age and history cannot come from a node RPC alone. The counterparty guard is a client-side module and is not wired into the main payment flow.

  7. Mandates on one deployment share the contract's NEP-141 balance, and isolation is bookkeeping. You also have to trust the token contract itself.

  8. There is no migrate function and the full-access key was burnt at initialisation. A fix means a new deployment and a withdrawal.

  9. Measured callback gas is 7 to 8 times under the pinned limit, but if one were ever lost, only the guardian can settle it, and only as transferred. A lost guardian key leaves it locked. Other mandates are not affected.

  10. Up to what is left of the budget, to allowed recipients, under the per-payment cap. Scrip bounds the loss. It does not make theft impossible.

10 plates, one per limit, standing on the one thing that does hold: the contract checks spent + reserved + amount against the budget on every payment. Hover, focus or press a limit to lift its plate. Drag to turn.

09Next

The agent can be fooled.The budget cannot.

Try it live on testnet

Give agents a budget, not your keys.

Try the rule machine

Built with Claude Code (planning, review, code) and Antigravity CLI with Claude Sonnet 5.5 and Gemini (delegated implementation). Real-model tests ran qwen3, llama3.2 and qwen2.5 through Ollama. Scrip, a NEAR hackathon project. Every number on this page is labelled with its source and date. Nothing here is financial advice.