Defining AI delegation in onchain finance with Agent Role Contracts
Tomohiro Iida ·

Consider a company or DAO using AI to prepare payments. The agent reads an invoice, identifies a recipient and amount, and prepares a transaction proposal for a wallet. Alongside checking the arithmetic, the team needs to know whether that proposal matches the assigned task and its approval conditions.
Key takeaways
- ARC makes discrepancies between what was delegated, what was proposed and what was reviewed checkable before funds move. Signing and sending belong to a separate execution system.
- The finance profile compares declarations for chain, recipient, asset and amount. A proposal with a recipient outside the allowlist failed with FINANCE_RECIPIENT_NOT_ALLOWED and passed again once restored (local checks on fictional data).
- Reducing the amount by one base unit after review still failed with FINANCE_SUBJECT_MISMATCH. Whether a real person reviewed it, or an approver had authority, must be confirmed through a separate authenticated path.
- Evidence age, partial execution, external side effects and authority drift over time were not tested in this run and are treated as unverified.
- ARC PASS is not permission to execute. Cumulative budget, duplicate-execution prevention, correspondence with the actual transaction bytes and on-chain finality belong to the treasury and wallet side.
Agent Role Contracts (ARC), the open-source checker published by Netsujo, validates consistency among declarations about agent roles, authority, tasks and reviews. Its optional finance profile checks payment and token-allowance proposals against declared conditions. ARC runs offchain; signing and broadcasting belong to a separately controlled execution system.
Its value in onchain finance is making discrepancies between what was delegated, what was proposed and what was reviewed checkable before funds move.[1]
Carry a payment limit through to the proposal
Take a fictional company policy: pay a recipient on the company's policy allowlist using a specified token on a specified chain, with a limit of 500 tokens per proposal. A proposal for 250 tokens can still violate that policy if it selects the wrong chain or recipient.
ARC's finance profile checks declarations for the chain, sender, recipient or token spender, asset, amount and fee ceiling. The role bundle and task are part of the inputs, connecting the proposal to the declared delegation.[1]
The policy and role bundle must come through a trusted path independent of the agent generating the proposal. If the agent can freely change both the proposal and the limit, it can make an unwanted proposal internally consistent.
The 500-token ceiling also applies to each proposal separately. An execution system needs a budget ledger or equivalent control to account for repeated proposals. Defining this boundary early makes it clear which checks ARC performs and which limits the treasury system must enforce.
An observed check against an unlisted recipient
On November 3, 2026, from 15:29:52 to 15:29:55 JST, we ran local declaration checks against the public source. The environment was Linux x86_64, Node.js v24.22.1 and npm 11.9.0. We retrieved 33 required files from the referenced commit and verified their Git blob hashes. The standard finance demo, Safe demo and additional checks each completed with exit code 0. The checks used a fixed evaluation clock on January 1, 2030; the time above records when the programs actually ran.
The key inputs were as follows. Addresses are fictional and abbreviated in the table. Assuming a token with six decimal places, these values correspond to the illustrative 250-token proposal and 500-token ceiling.
| Field | Policy or proposal input |
|---|---|
| Allowed and proposed chain | Both "11155111" |
| Allowed and proposed recipient | Both 0x2222...2222 |
| Proposed amount | "250000000" base units |
| Per-proposal transfer ceiling | "500000000" base units |
We changed only the recipient to 0x9999...9999, retaining the policy and approval declarations. The additional check produced this output:
A1 Original recipient 0x2222...2222: PASS
A2 Recipient changed to 0x9999...9999; policy unchanged: FAIL [FINANCE_RECIPIENT_NOT_ALLOWED]
A3 Restore original proposal; policy and evidence unchanged: PASSThe repair restored the original proposal, including its recipient. It did not widen the allowlist or alter approval declarations to pass the check. What was measured is that restoring the original input returns to PASS. Separating correction of a proposal from an authorized change to company payment policy is a design expectation, not something this run confirmed. These were local checks of fictional data, with no transfer or authentication of a real approver.
Bind the reviewed content to the content being handed onward
A payment proposal may change after review. A record saying “reviewed” is difficult to reuse reliably if the recipient or amount has since been replaced.
ARC computes a subject digest covering the complete role and routing bundle, task, financial policy and transaction proposal. Simulation, review and human-approval declarations bind to that subject. Changing the proposal or policy while retaining declarations for the old subject produces a mismatch. The public demo includes a case where even reducing the amount invalidates the previous binding.[1][2]
1. Treasury payment within the declared limit: PASS
5. Amount changes after review: FAIL (expected): FINANCE_SUBJECT_MISMATCHThis excerpt is actual output from the standard demo. Reducing the amount from "250000000" to "249999999" still produced a mismatch with the previous review and other declarations.
This design checks the identity of the approved subject without assuming a small edit is harmless. A digest establishes correspondence between content; an authenticated external approval process must establish who actually reviewed it and whether the approver had authority.
Role separation follows the same pattern. Declare separate proposer and reviewer roles, then assign them to suitably independent participants in the real workflow. Declaration checking and operational verification of identity and independence work together.
Token allowances and Safe proposals add specific checks
ERC-20 defines both transfer, for moving tokens, and approve, which lets a specified spender use tokens up to an allowance. An allowance proposal therefore needs its own review of the spender and amount, distinct from the amount being transferred immediately.[3]
For supported standard ERC-20 allowance proposals, ARC checks declared spender and allowance limits and refuses the uint256 maximum used as an unlimited allowance. It relies on the supplied current-allowance declaration; the integration must obtain the actual value from the chain.[1]
The optional Safe profile matches a supported single CALL declaration against the approved intent, including the target, native value, calldata, operation, chain, Safe address and nonce. For an ERC-20 transfer, the call target is the token contract; the recipient and amount are encoded in calldata. That correspondence can be checked too.[1]
This has a different responsibility from Safe's signature and execution checks. In the normal Safe owner-signature transaction path, Safe verifies the required owner signatures. ARC's bounded profile excludes batches, delegatecall, complete transaction serialization and Safe transaction-hash verification.[1][4]
In an additional check, we changed only the final calldata nibble from 0 to 1. In this fixture, that increases the transfer amount by one base unit. Keeping the intent unchanged produced:
B1 Original Safe CALL declaration: PASS
B2 Change last calldata nibble 0 -> 1; intent unchanged: FAIL [SAFE_DATA_MISMATCH]
B3 Restore original Safe CALL; intent unchanged: PASSA workflow that builds the reviewed intent and wallet proposal separately can use this kind of correspondence check. The observation covers supplied CALL declarations; the integration must still verify correspondence with the actual transaction bytes to be signed.
Integrate it with explicit responsibilities
The following division is a starting point for a treasury or payment integration.
| Question | ARC's scope | Integration responsibility |
|---|---|---|
| Does the proposal satisfy the conditions? | Compare supplied declarations for chain, counterparties, asset, amount and other supported limits | Protect the policy, validate recipient registration, check actual balances and fees |
| Was the same content reviewed? | Check simulation, review and approval declaration bindings to the subject | Authenticate participants and authority; obtain and verify real simulation evidence |
| What will reach the wallet? | Match a supported single Safe CALL declaration to the intent | Match the actual transaction bytes to be signed; control keys and wallet permissions |
| What happened afterward? | Check the supplied execution-receipt declaration against the subject, chain, nonce and related conditions | Obtain chain evidence; check finality, balance changes and duplicate execution; retain audit records |
An ARC PASS grants no execution permission. A matching execution-receipt declaration also requires separate verification that the evidence came from a real chain.[1]
We also checked the aggregate-budget boundary. Rechecking the same "250000000"-base-unit proposal three times against a "500000000" per-proposal ceiling produced three PASS results. These were repeated checks of the same fictional proposal, recording no expenditure or budget consumption. The observation makes the need for a separate budget ledger and duplicate-execution control concrete.
The integration value is making the object of each check explicit and transferable between participants and tools. Combined with wallet-enforced limits, this can help a team inspect the correspondence from business instruction to execution target. Whether it reduces rework or review effort must be measured in the actual workflow.
Failure scenarios to consider, and what this record shows
The four situations below are design scenarios for an AI that prepares payments in onchain finance. They are not reports of incidents that occurred. Each item ends with whether this run's record contains an observation for it.
- Authority overreach. The task allows payment to approved recipients, but the AI proposes the same amount to a different party. A declaration check examines the consistency of recipient, chain and amount declarations with the policy. This record: measured. A changed recipient and a chain switch were rejected.
- Post-review change and approval expiry. The amount changes after review, or time passes after approval. A declaration check examines the binding of declarations to a subject hash; the source also has an age limit for simulation, review and approval evidence. This record: the amount change was rejected (measured). Evidence age was not exercised, so approval expiry is unverified.
- Partial execution. Only some of several transactions are executed. This is out of scope for the declaration check; the execution side reconciles outcomes. This record: no observation, so unverified.
- External side effects. An unlimited token allowance remains with a third party, separate from the transfer. A declaration check refuses unlimited approvals. This record: measured. The refusal was observed. Post-execution chain state is out of scope.
"Measured" means an observation within the recorded cases; "not exercised" and "unverified" mean this run did not test them. The problem of authority drifting out of step with actual state over time is covered in a separate article.
Expected versus observed
Design expectations beside what the record actually shows.
- Chain switch. Expected: rejected. Observed: failed with FINANCE_CHAIN_NOT_ALLOWED.
- Amount one base unit over the limit. Expected: rejected. Observed: failed with FINANCE_TRANSFER_LIMIT_EXCEEDED.
- Unlimited token allowance. Expected: rejected. Observed: failed with FINANCE_UNLIMITED_APPROVAL_REFUSED.
- Amount change after review. Expected: old declarations rejected. Observed: failed with FINANCE_SUBJECT_MISMATCH.
- New declarations bound to the changed proposal. Expected: passes. Observed: PASS.
- Execution receipt with a different nonce. Expected: rejected. Observed: failed with FINANCE_EXECUTION_NONCE_MISMATCH.
- Recipient outside the allowlist. Expected: rejected, and passes when restored. Observed: failed with FINANCE_RECIPIENT_NOT_ALLOWED; PASS after restoring.
- Last calldata nibble of the Safe call changed. Expected: rejected, and passes when restored. Observed: failed with SAFE_DATA_MISMATCH; PASS after restoring.
- Same proposal checked three times. Expected: cumulative total not counted (a design assumption). Observed: PASS all three times, consistent with the assumption.
- Evidence age limit (simulation, review and approval declarations). Expected: rejected when exceeded (per the source). Observed: not exercised. This article did not run an input past the limit.
- Partial execution, external side effects, authority drift over time. Expected: out of ARC's scope, or not handled. Observed: unverified.
PASS means only that the supplied declarations are consistent. It is not permission to execute.
What review corrected, and why
When a draft of this article went through an AI review separate from the writer (not a formal independent QC), some statements turned out to be stronger than the record. We corrected them and keep the reasons here.
- The draft said the run confirmed that correcting a proposal can be separated from approving a policy change. The record shows only that restoring the original input returns to PASS. The separation is now stated as a design expectation.
- A cover-image label said a mismatched proposal is "not passed to execution". ARC only checks; it does not stop a hand-off. What happens next is decided by the integrating system. The cover is now a text-free diagram, and its alt text says that what happens to a mismatched proposal is decided outside ARC.
- The draft gave no way to reproduce the additional checks. The two standard demos can be reproduced from a pinned commit, but the script behind the additional checks is not in the public repository. The article now separates what can and cannot be reproduced.
The additional checks were designed for a reason as well. The policy and approval declarations stayed unchanged, one input was changed per run, and the original input was then restored. That ties each failure to the single changed input. Widening the policy until the check passed was not done, because that would not be a repair.
A history of defects found during ARC's own development is not part of this run's record, and this article does not cover it.
Evaluate one payment proposal first
Start with one routine payment. Define the proposer, reviewer, approver, allowed chain, recipient and amount ceiling. Check both a valid proposal and deliberate failures: a wrong chain, an excessive amount and a change after review. The public finance demo provides fictional inputs for starting that evaluation.[2]
Record whether the intended mismatches were detected, whether valid proposals were obstructed and whether participants understood the correction required. Rejection counts alone are a weak outcome measure. Connecting real funds is a later validation stage covering authentication, checks immediately before signing, aggregate budgets, duplicate-execution prevention and reconciliation.
Reproduce the demos, and what we did not test
The two standard demos can be reproduced from a pinned commit of the public repository. You need Git, Node.js and npm. The recorded run used Node.js v24.22.1 and installed no dependencies; only the clone uses the network.
git clone https://github.com/suirindo/agent-role-contracts.git
cd agent-role-contracts
git checkout --detach a2fa013deafb81fb973fadf1e2e1a2adb60e693e
npm run demo:finance --silent
npm run demo:safe --silentCheck that the exit code is 0 and that the finance demo prints eight scenarios in order, with five failing with the expected codes and three passing. The individual expected rejections appear inside a successful run. The script that ran the additional recipient, calldata and repeated-check cases is not in the public repository; it exists only in Netsujo's run record, and there is no public URL for it yet. Those cases are recorded observations, not checks a reader can reproduce with the same script.
Some items were not tested in this run. The public source contains an evidence-age setting (max_evidence_age_seconds) and an error code (FINANCE_EVIDENCE_EXPIRED), but this run did not exercise them. Partial execution, external side effects and authority drift over time were also out of scope, and remain unverified.
Netsujo publishes ARC itself and shows, with run results, what a declaration check can examine and what the integrating system carries. The observations here do not show reduced effort or fewer incidents from adoption. Even so, writing down what the AI may do as declarations and checking one payment proposal is a useful input to an adoption decision.
To discuss a concrete workflow, share the target business process, existing approval path, chain and wallet through Netsujo's contact page. The public implementation is available on GitHub.
Sources
Specifications checked on November 3, 2026, against ARC commit a2fa013deafb81fb973fadf1e2e1a2adb60e693e. The payment scenario is fictional.