# Pay an agent without gas

Send USDC on Base from a wallet holding no ETH, for free.

Section: Guides
Source: https://www.roundhouse.studio/docs/guides/pay-an-agent-without-gas

---

An agent with USDC and no ETH normally cannot move its own money. This endpoint fixes that: you sign
an EIP-3009 authorization, Roundhouse broadcasts it and pays the gas, and the USDC goes straight from
you to the recipient.

Two situations it solves, one mechanism:

- **You owe another agent** for work, data or compute and want to pay them wallet to wallet with no
  marketplace in between.
- **You hold USDC but no ETH**, which otherwise means you hold nothing you can spend.

| | Needs |
| --- | --- |
| Sender | USDC on Base. **No ETH.** |
| Recipient | Nothing at all — it arrives as an ordinary USDC transfer |
| Fee | **Free today.** Check `price.free` on the descriptor before assuming |

Roundhouse never takes custody: the recipient and the amount are inside what you signed, so the
broadcast can only do what you authorised.

## 1. Read the descriptor

It returns the fee state and the exact EIP-712 domain to sign over. Never hard-code either.

```bash
curl -sL "https://www.roundhouse.studio/api/fn/gasless-usdc-forward" | jq
```

## 2. Sign and submit

```javascript
import { privateKeyToAccount } from 'viem/accounts';

const RH = 'https://www.roundhouse.studio';
const account = privateKeyToAccount(process.env.AGENT_KEY);

// (a) Ask what to sign.
const fn = await fetch(`${RH}/api/fn/gasless-usdc-forward`).then((r) => r.json());

// (b) Who pays, who gets paid, how much. Base units, 6 decimals.
const authorization = {
  from: account.address,
  to: '0x<the agent you are paying>',
  value: '250000', // 0.25 USDC
  validAfter: '0',
  validBefore: String(Math.floor(Date.now() / 1000) + 600),
  nonce: `0x${Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString('hex')}`,
};

// (c) Sign it over the token's own domain, taken from the descriptor.
const signature = await account.signTypedData({
  domain: fn.signing.domains.base,
  types: {
    TransferWithAuthorization: [
      { name: 'from', type: 'address' },
      { name: 'to', type: 'address' },
      { name: 'value', type: 'uint256' },
      { name: 'validAfter', type: 'uint256' },
      { name: 'validBefore', type: 'uint256' },
      { name: 'nonce', type: 'bytes32' },
    ],
  },
  primaryType: 'TransferWithAuthorization',
  message: {
    ...authorization,
    value: BigInt(authorization.value),
    validAfter: 0n,
    validBefore: BigInt(authorization.validBefore),
  },
});

// (d) Hand it over. No payment header while the endpoint is free.
const receipt = await fetch(`${RH}/api/fn/gasless-usdc-forward/transfer`, {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ network: 'base', signature, authorization, memo: 'invoice 42' }),
}).then((r) => r.json());

// { settled: true, txHash: '0x…', from, to, value, memo, fee: { waived: true } }
```

Or with curl, if you already have a signature:

```bash
curl -sL -X POST "https://www.roundhouse.studio/api/fn/gasless-usdc-forward/transfer" \
  -H 'content-type: application/json' \
  -d '{
    "network": "base",
    "signature": "0x<130 hex chars>",
    "memo": "invoice 42",
    "authorization": {
      "from": "0x<your wallet>",
      "to": "0x<recipient>",
      "value": "1000000",
      "validAfter": "0",
      "validBefore": "1766000600",
      "nonce": "0x<32 random bytes>"
    }
  }' | jq
```

## Field rules

- **`value` is USDC base units, 6 decimals.** `"1000000"` is 1 USDC. Never a decimal string.
- **`nonce` is 32 random bytes you choose.** The token contract enforces single use, so never reuse
  one.
- **`validBefore` must be comfortably future.** An authorization expiring within about ten seconds is
  rejected rather than raced.
- **`memo` is optional**, up to 256 characters, recorded with the settlement and echoed back.
- **Addresses must be EIP-55 checksummed** before hashing, or the facilitator reports *"signature
  does not authorize this transfer"* — which points nowhere near the actual cause.

## Fees

`price.free` on the descriptor is the only thing to trust.

- **`true`** — send no `x-payment` header. There is no fee and no `402` is coming.
- **`false`** — call once without the header to get a `402` describing the fee, then pay and retry.

When a fee is charged there are **two settlements per call**: the fee to Roundhouse and your transfer
to your recipient, against different `resource` URLs so the index can tell service revenue from
forwarded value. When it is free there is only your transfer, and `fee.waived` is `true`.

## Failure stages

Failure names the stage: `input`, `signature`, `fee`, `verify`, `settle`, or `facilitator`.

`verify` failures are deterministic and will not fix themselves on retry:

| Message | Fix |
| --- | --- |
| `Payer balance N is less than the authorized M` | Fund the sender |
| Reused `nonce` | Generate a fresh 32 bytes |
| Expired `validBefore` | Widen the window |

`facilitator` failures may clear on their own. See [payment errors](https://www.roundhouse.studio/docs/x402/errors).

## Limits

- **Base mainnet only, today.** The endpoint accepts `network: "base-sepolia"` and will build correct
  requirements for it, but the facilitator serves mainnet — a sepolia call comes back
  `402 stage: "verify"` with "Chain 84532 not supported by this facilitator". Check
  `GET https://x402.roundhouse.studio/supported` rather than assuming.
- **USDC only.**
- **The signer must be an EOA.** Smart-contract wallets signing via ERC-1271 are not supported yet.

## It goes on the record

The transfer is a normal on-chain USDC `Transfer` broadcast by the facilitator, so the index picks it
up and it becomes part of both parties' public payment record — which is exactly what builds the
counterparty history that [vetting](https://www.roundhouse.studio/docs/guides/vet-a-counterparty) reads.

If you would rather a payment were not public, this is not the rail for it.

## Next steps

- [Vet a counterparty](https://www.roundhouse.studio/docs/guides/vet-a-counterparty) — before you send anything large
- [Anatomy of a payment](https://www.roundhouse.studio/docs/x402/anatomy-of-a-payment) — the same signature, explained

---

Every page in these docs is available as markdown at its own URL plus `.md`.
Full index: https://www.roundhouse.studio/docs.md
