I would like to add a set of Peer Cash actions so an agent can turn its Base USDC into fiat, and I want to check the scope with you before opening the PR.
What Peer Cash is
Peer Cash is an offramp protocol on Base. The agent's wallet is the maker: its USDC becomes a protocol-held order, a buyer pays fiat to the agent's own Venmo, Revolut, Wise, Zelle, PayPal, Cash App, Chime, or Monzo handle and proves the payment with TEE-TLS, and the escrow releases the USDC. Pricing is the live Chainlink oracle rate at zero spread. There is no centralised offramp provider in the middle.
Agentkit already gives an agent custodied funds on Base. Today the only exit is another token. This adds a fiat exit the agent can drive itself.
Proposed actions
agentkit-core/src/actions/PeerCashAction/, all walletOptional: true and smartAccountRequired: false:
| Action |
What it does |
peer_cash_capabilities |
payout platforms, currencies per platform, payee handle formats, amount bounds |
peer_cash_estimate |
fiat received at the live oracle rate, plus a 30-day median fill ETA |
peer_cash_prepare_cashout |
unsigned [approve, createDeposit] for send_transaction |
peer_cash_prepare_access_policy |
the follow-up Venmo, Cash App, and PayPal orders require |
peer_cash_order |
one order's state, fills, and next actions |
peer_cash_orders |
every order owned by an address, defaulting to the agent's own |
peer_cash_prepare_topup |
unsigned [approve, addFunds] for a live order |
peer_cash_prepare_withdraw |
unsigned transactions that unwind an order |
The custody question, which is why I am asking first
@zkp2p/cash has no signing path in its agent surface by design. Mutating verbs return unsigned { to, data, value, chainId } plans and the host signs. That maps onto your existing send_transaction and get_transaction_status actions without Peer ever seeing a key, and it is how the Peer MCP server and the other Peer integrations are built.
So the question is which shape you want:
- Prepare only, as above, with the agent chaining
send_transaction. This is what I have built.
- An additional
peer_cash_cashout that calls wallet.sendTransaction for both steps and then resolves the deposit id from the receipt.
I built (1) because every action can be verified end to end against production without moving funds, and because it keeps the custody boundary where the rest of Agentkit already puts it. (2) is straightforward to add on top if you would rather have the one-shot verb.
Three things that would change your tree
A dependency. @zkp2p/cash pulls @zkp2p/sdk and @relayprotocol/relay-sdk. viem is already a dependency and the existing "viem": "2" range covers what the SDK needs. Given the dependency pruning in 1.0, say the word if you would rather this shipped as a separate toolkit package than inside the core action set.
One tsconfig line. agentkit-core typechecks at target: es2020. The newer ox that viem resolves to ships .ts sources that need es2022 for String.replaceAll and the Error cause option, so bun run check fails without "target": "es2022" in agentkit-core/tsconfig.json. I scoped it to that one file rather than tsconfig.base.json. Typecheck, build, and the existing platform smoke test all pass with it.
An attribution tag. The client is constructed with referrer: "0xgasless-agentkit", which appends that string to the ERC-8021 attribution suffix on the prepared calldata, next to the peer-cash marker the SDK adds regardless. It carries no funds, grants no permissions, and exists so Peer can see which orders came from Agentkit. Say so and I will drop it.
Chain scope
Peer Cash settles on Base (8453) only. Base is already in supportedChains and USDC is already in tokenMappings[8453]. Prepared transactions carry chainId: 8453, so an agent must be configured on Base to submit them. The read actions work in any mode, including with no wallet at all.
Verification
On the branch I have ready: bun run check and bun run build pass, biome lint and biome check are clean on the new files, and bun run lint reports the same 30 pre-existing errors as main with none in the new directory. I added test/peer-cash.smoke.mjs in the same style as platform.smoke.mjs. It is offline by default, covering registration, wallet-optional dispatch, argument schemas, and the typed error surface, and reads Peer production behind PEER_CASH_SMOKE_LIVE=1. Both smoke tests pass in both modes. Nothing is ever signed or broadcast.
All eight actions have been run against Peer production with no wallet configured. Discovery returned 8 payout platforms across 13 currencies on chain 8453 at 0 bps spread; a 250 USDC estimate returned 215.67 EUR; the cash-out, top-up, withdrawal, and access-policy paths each returned the expected unsigned transactions with chainId: 8453; a bad order id returned Error: Peer Cash order failed. [ORDER_NOT_FOUND] ... This error is retryable.
Happy to open the PR against main as soon as you confirm the shape, or to adjust it first if you would rather have something different.
I would like to add a set of Peer Cash actions so an agent can turn its Base USDC into fiat, and I want to check the scope with you before opening the PR.
What Peer Cash is
Peer Cash is an offramp protocol on Base. The agent's wallet is the maker: its USDC becomes a protocol-held order, a buyer pays fiat to the agent's own Venmo, Revolut, Wise, Zelle, PayPal, Cash App, Chime, or Monzo handle and proves the payment with TEE-TLS, and the escrow releases the USDC. Pricing is the live Chainlink oracle rate at zero spread. There is no centralised offramp provider in the middle.
@zkp2p/cash, MIT, https://github.com/zkp2p/peer-cashAgentkit already gives an agent custodied funds on Base. Today the only exit is another token. This adds a fiat exit the agent can drive itself.
Proposed actions
agentkit-core/src/actions/PeerCashAction/, allwalletOptional: trueandsmartAccountRequired: false:peer_cash_capabilitiespeer_cash_estimatepeer_cash_prepare_cashout[approve, createDeposit]forsend_transactionpeer_cash_prepare_access_policypeer_cash_orderpeer_cash_orderspeer_cash_prepare_topup[approve, addFunds]for a live orderpeer_cash_prepare_withdrawThe custody question, which is why I am asking first
@zkp2p/cashhas no signing path in its agent surface by design. Mutating verbs return unsigned{ to, data, value, chainId }plans and the host signs. That maps onto your existingsend_transactionandget_transaction_statusactions without Peer ever seeing a key, and it is how the Peer MCP server and the other Peer integrations are built.So the question is which shape you want:
send_transaction. This is what I have built.peer_cash_cashoutthat callswallet.sendTransactionfor both steps and then resolves the deposit id from the receipt.I built (1) because every action can be verified end to end against production without moving funds, and because it keeps the custody boundary where the rest of Agentkit already puts it. (2) is straightforward to add on top if you would rather have the one-shot verb.
Three things that would change your tree
A dependency.
@zkp2p/cashpulls@zkp2p/sdkand@relayprotocol/relay-sdk.viemis already a dependency and the existing"viem": "2"range covers what the SDK needs. Given the dependency pruning in 1.0, say the word if you would rather this shipped as a separate toolkit package than inside the core action set.One tsconfig line.
agentkit-coretypechecks attarget: es2020. The neweroxthat viem resolves to ships.tssources that needes2022forString.replaceAlland theErrorcauseoption, sobun run checkfails without"target": "es2022"inagentkit-core/tsconfig.json. I scoped it to that one file rather thantsconfig.base.json. Typecheck, build, and the existing platform smoke test all pass with it.An attribution tag. The client is constructed with
referrer: "0xgasless-agentkit", which appends that string to the ERC-8021 attribution suffix on the prepared calldata, next to thepeer-cashmarker the SDK adds regardless. It carries no funds, grants no permissions, and exists so Peer can see which orders came from Agentkit. Say so and I will drop it.Chain scope
Peer Cash settles on Base (8453) only. Base is already in
supportedChainsand USDC is already intokenMappings[8453]. Prepared transactions carrychainId: 8453, so an agent must be configured on Base to submit them. The read actions work in any mode, including with no wallet at all.Verification
On the branch I have ready:
bun run checkandbun run buildpass,biome lintandbiome checkare clean on the new files, andbun run lintreports the same 30 pre-existing errors asmainwith none in the new directory. I addedtest/peer-cash.smoke.mjsin the same style asplatform.smoke.mjs. It is offline by default, covering registration, wallet-optional dispatch, argument schemas, and the typed error surface, and reads Peer production behindPEER_CASH_SMOKE_LIVE=1. Both smoke tests pass in both modes. Nothing is ever signed or broadcast.All eight actions have been run against Peer production with no wallet configured. Discovery returned 8 payout platforms across 13 currencies on chain 8453 at 0 bps spread; a 250 USDC estimate returned 215.67 EUR; the cash-out, top-up, withdrawal, and access-policy paths each returned the expected unsigned transactions with
chainId: 8453; a bad order id returnedError: Peer Cash order failed. [ORDER_NOT_FOUND] ... This error is retryable.Happy to open the PR against
mainas soon as you confirm the shape, or to adjust it first if you would rather have something different.