Agent Payment Stack: Reference Architecture
Four on-chain primitives. Three composable flows. Everything you need to build agent-to-agent payments on Ergo — from a single API call to a full community currency.
The Four Primitives
Reserve · Note · Tracker · Predicate
Reserve
Capital backing
An on-chain contract that holds ERG or tokens as collateral. Anyone can verify the reserve ratio at any time. The reserve is the bank — no middleman.
- Holds ERG / native tokens
- Auditable at any time
- Backs note issuance 1:1 or fractionally
- Redemption enforced by contract
{
// Reserve contract: allow withdrawal only
// if redemption note is burned
val noteBox = INPUTS(1)
val burnCheck = noteBox.tokens.size == 0
sigmaProp(burnCheck && SELF.R4[Long].get > 0)
}Note
Programmable IOU
A bearer token representing a claim against a reserve. Notes can be transferred, split, and redeemed. The acceptance predicate defines who can receive or redeem the note.
- Bearer instrument — no identity required
- Transferable peer-to-peer
- Redeemable against reserve contract
- Denomination in any token
{
// Note: transferable by holder,
// redeemable against reserve
val holder = SELF.R4[GroupElement].get
val isHolder = proveDlog(holder)
val isRedemption = INPUTS.exists { box =>
box.tokens.exists(_._1 == reserveId)
}
isHolder || isRedemption
}Tracker
On-chain accounting
Tracks cumulative balances, credit usage, and redemption history across all notes issued from a reserve. Think of it as a public ledger that anyone can verify.
- Tracks total issued vs redeemed
- Enforces credit limits
- Publicly auditable
- Composable with other contracts
{
// Tracker: ensure running total
// doesn't exceed credit limit
val limit = SELF.R4[Long].get
val used = SELF.R5[Long].get
val newUsed = used + INPUTS(0).value
sigmaProp(newUsed <= limit)
}Acceptance Predicate
Programmable trust rule
The condition under which an agent will accept a note as payment. Conditions can include task hash verification, deadline checks, credential proofs, or any on-chain data.
- Arbitrary acceptance conditions
- Task hash verification
- Deadline enforcement
- Composable with Sigma proofs
{
// Accept note only if:
// 1. Task hash matches R4
// 2. Deadline not passed
val taskHash = SELF.R4[Coll[Byte]].get
val deadline = SELF.R5[Int].get
val inputHash = INPUTS(0).R4[Coll[Byte]].get
val onTime = HEIGHT <= deadline
sigmaProp(taskHash == inputHash && onTime)
}Sample Flows
Three composable flows
Flow A: Agent buys API call
One call. One proof. No persistent account.
- 1
Agent creates note
0.001 ERG face value, provider address in R4
- 2
Provider checks predicate
Verifies note value, reserve backing, deadline
- 3
Provider delivers response
Returns API result, marks note as spent
- 4
Settlement
Note burned, ERG released from reserve to provider
// Fleet SDK: create a note for API payment
import { TransactionBuilder, OutputBuilder, SAFE_MIN_BOX_VALUE } from "@fleet-sdk/core"
const noteBox = new OutputBuilder(
1_000_000n, // 0.001 ERG
NOTE_CONTRACT_ADDRESS
)
.setAdditionalRegisters({
R4: SGroupElement(providerPubKey), // recipient
R5: SLong(BigInt(HEIGHT + 100)), // deadline
R6: SColl(SByte, taskHashBytes), // task identifier
})
const tx = new TransactionBuilder(currentHeight)
.from(inputs)
.to(noteBox)
.sendChangeTo(agentAddress)
.payMinFee()
.build()Flow B: Agent pays on credit
Reserve deployed. Notes issued. Tracker monitors.
- 1
Deploy reserve
Lock ERG in reserve contract with credit limit
- 2
Issue notes
Create notes against reserve up to credit limit
- 3
Agent spends notes
Notes transferred to providers, tracker updated
- 4
Auto-settlement
When threshold hit, reserve settles outstanding notes
// Deploy a reserve with 10 ERG, 100 ERG credit limit
const reserveBox = new OutputBuilder(
10_000_000_000n, // 10 ERG collateral
RESERVE_CONTRACT_ADDRESS
)
.setAdditionalRegisters({
R4: SLong(100_000_000_000n), // 100 ERG credit limit
R5: SLong(0n), // issued so far
R6: SGroupElement(ownerKey), // reserve controller
})
// Issue a note against the reserve
const noteBox = new OutputBuilder(
1_000_000_000n, // 1 ERG face value
NOTE_CONTRACT_ADDRESS
)
.setAdditionalRegisters({
R4: SGroupElement(agentKey), // holder
R5: SColl(SByte, reserveId), // backing reserve
})Flow C: Community reserve + tracker
A local marketplace, a compute co-op, an agent network.
- 1
Community deploys reserve
ERG pooled by community members
- 2
Issue community notes
Members receive notes proportional to contribution
- 3
Local commerce
Notes accepted within community, predicates define rules
- 4
Redemption
Members redeem notes for ERG from reserve anytime
// Community reserve: multiple funders
const communityReserve = new OutputBuilder(
TOTAL_POOLED_ERG,
MULTISIG_RESERVE_CONTRACT
)
.setAdditionalRegisters({
R4: SColl(SGroupElement, memberKeys), // governance
R5: SInt(3), // 3-of-5 threshold
R6: SColl(SByte, communityTokenId), // community token
})
// Acceptance predicate: only members of community
const memberPredicate = `{
val isMember = CONTEXT.dataInputs(0)
.R4[Coll[GroupElement]].get
.exists(pk => proveDlog(pk))
sigmaProp(isMember)
}`SDKs & Tools
Start building today
Fleet SDK
TypeScript / JS
Build transactions in your browser or Node.js. Best starting point for agent payments.
sigma-rust
Rust
High-performance signing and serialization. Ideal for production agent infrastructure.
What's live
Stack status
Five layers are live. Two are open problems. Join the conversation on GitHub — these are the most interesting unsolved problems in agent money.