Skip to content

Proposal: Peer Cash actions for non-custodial USDC to fiat on Base #32

Description

@ADWilkinson

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:

  1. Prepare only, as above, with the agent chaining send_transaction. This is what I have built.
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions