# Pontica for agents

OBOL protocol · agent guide · https://pontica.gg/protocol/agents

OBOL credits live on Robinhood Chain (chain id 4663), and each one activates to $1 of inference at OpenRouter's
list price. An agent can get OBOL, activate it for its own wallet, sign once for an API key and call any model through
one OpenAI compatible endpoint, with no person at a checkout in between.

## The basic idea

OBOL is an ERC-20 token with 6 decimals, like USDG. The Mint issues it one for one against USDG that goes straight to
the treasury, the wallet that pays OpenRouter for the inference. Holders can list OBOL below $1 on the book; a buy
fills the deepest discount first and mints the rest at $1, so a buyer never pays more than list.

Activation burns OBOL and credits the same amount, less the activation fee, to the API balance of the wallet you name.
That wallet signs one message to get its key. Every request then draws on its balance at the model's OpenRouter list
price. Nothing on chain turns OBOL back into USDG.

## How an agent uses it

### 1. Get OBOL

Buy with `buyWith` on the Exchange and set `mintRemainder`: the book fills every ask that beats $1 after the fee, and
whatever it cannot beat is minted at $1 through the Mint in the same transaction. Quote first with
`getBestQuote(usdgIn, maxFills)`, which returns exactly what `buyWith` will do. Approve exactly the USDG you spend to
the Exchange.

```js
// EXCHANGE and USDG are in the contract list below; USDG and OBOL have 6 decimals
const usdgIn = parseUnits('20', 6);
const [book, mintUsdg, mintOut] = await pub.readContract({ address: EXCHANGE, abi: exchangeAbi,
  functionName: 'getBestQuote', args: [usdgIn, 64n] });
const minCreditOut = ((book.creditOut + mintOut) * 995n) / 1000n; // other buys may land first
await wallet.writeContract({ address: USDG, abi: erc20Abi, functionName: 'approve', args: [EXCHANGE, usdgIn] });
await wallet.writeContract({ address: EXCHANGE, abi: exchangeAbi, functionName: 'buyWith', args: [{
  usdgIn, minCreditOut, recipient: account.address, maxFills: 64n, mintRemainder: true, referrer: zeroAddress,
}] });
```

`BuyArgs` is `(uint256 usdgIn, uint256 minCreditOut, address recipient, uint256 maxFills, bool mintRemainder, address
referrer)`; `minCreditOut` is checked against the OBOL from the book plus the OBOL minted. The ABIs are at
https://pontica.gg/protocol/abi/exchange.json (also obol, mint, staking, funds and erc20).

To skip the book, mint directly: `fund(usdgIn, recipient)` on the Mint (approve USDG to the Mint first), or
`fundETH(minUsdgOut, recipient, deadline)` with ETH as the value, which swaps on the WETH/USDG 0.01% pool and mints
what the swap returns. Quote ETH by calling `fundETH` with `eth_call`; there is no quoter on this chain.

The classic `buy` and `buyAndActivate` are book only. Only `buyWith` and `buyAndActivateWith` with `mintRemainder`
promise that you never pay more than $1 for an OBOL.

### 2. Activate what it needs

Call `activate(amount, beneficiary)` on the OBOL token. The whole amount is burned from the caller; the beneficiary is
credited the amount less the activation fee. `previewActivation(amount)` returns `(credited, fee)` first. To buy and
activate in one transaction use `buyAndActivateWith(args, beneficiary)` on the Exchange, or
`fundAndActivate(usdgIn, beneficiary)` and `fundETHAndActivate(minUsdgOut, beneficiary, deadline)` on the Mint.

The API balance of a wallet is `creditedOf(wallet)` on the OBOL token, the running total of everything credited to it.
The gateway reads it with one call.

### 3. Sign once for API access

Sign this Sign-In with Ethereum text with the wallet that holds the balance (`personal_sign` over the UTF-8 bytes),
then send it to `POST https://pontica.gg/api/v1/keys` as `{ address, message, signature }`. Lines are joined with a newline and there is
no trailing newline. The nonce is 16 lowercase hex characters, the times are UTC with seconds, and the message expires
at most 10 minutes after it was issued.

```
pontica.gg wants you to sign in with your Ethereum account:
{address, EIP-55 checksum}

Issue a Pontica API key for this wallet. This retires any key it already has.

URI: https://pontica.gg
Version: 1
Chain ID: 4663
Nonce: {16 lowercase hex}
Issued At: {e.g. 2026-09-22T09:30:00Z}
Expiration Time: {Issued At + 10 minutes}
```

The answer is `201` with `{ key, hash, base_url, address, activated_usd, used_usd, limit_usd, remaining_usd, replaced,
note }`. The secret `key` appears in this answer only; keep `hash` (the SHA-256 of the key) to sync later. A wallet has
one live key: issuing a new one retires the old one. Treat the signed message like a password until it expires.

### 4. Work

The base URL is `https://pontica.gg/api/v1` and the key is a bearer token. Any OpenAI or OpenRouter client works:
`POST /chat/completions` (streaming with `stream: true`) and `GET /models` for the live catalogue.

```js
import OpenAI from 'openai';

const client = new OpenAI({ baseURL: 'https://pontica.gg/api/v1', apiKey: process.env.PONTICA_API_KEY });
const reply = await client.chat.completions.create({
  model: 'anthropic/claude-sonnet-5',
  messages: [{ role: 'user', content: 'Plan the next task in three steps.' }],
});
```

### 5. Know your balance

`GET https://pontica.gg/api/v1/key` with the bearer answers OpenRouter's key object, where `limit_remaining` is what the key can still
spend, plus a `pontica` block: `{ address, activated_usd, used_usd, remaining_usd, retired, needs_sync }`.
`needs_sync: true` means an activation is not in the key's limit yet.

### 6. Refill

Activate more OBOL to the same wallet, then raise the key's limit to what the chain proves:
`POST https://pontica.gg/api/v1/keys/sync` with the bearer (or with the body `{ hash }`). No signature is needed; the answer is
`{ address, hash, activated_usd, used_usd, limit_usd, remaining_usd, synced }`. The key stays the same.

### 7. Get funded

People can fund an agent through AI Funding: a fund names the agent's wallet as its beneficiary, with a goal in OBOL, a
deadline and an optional daily cap. Once the fund succeeds, the beneficiary calls `draw(id, amount, to)` or
`drawAndActivate(id, amount, account)` on the Funds contract, at most the daily cap per UTC day (`drawable(id)` says
what is left today). If the deadline passes below the goal, backers take their OBOL back with `refund(id)`.

## Where the discount comes from

Only from holders who list OBOL below $1 with `sell(obolAtoms, price)`. Prices sit on a grid of 2.5% steps from 97.5%
of list down to 2.5%, and a buy takes the deepest discount first, the oldest ask first within a price. When the book is
empty there is no discount and every buy mints at $1. Sellers are paid in USDG and claim it with `claim(to)`.

## Fees and limits

The chain is the authority: read the live values from the views named here. A fee increase is announced by the owner
and takes force after one day (`pendingFee()` shows it); decreases apply at once.

| What | At launch | Live value |
|---|---|---|
| Book fee, inside the buyer's budget | 1.5% of book fills | `Exchange.feeBps()` |
| Referrer's share of the book fee | 50% of the fee | `Exchange.referralShareBps()` |
| Mint fee | 0% | `Mint.mintFeeBps()` |
| Activation fee | 5% | `Obol.activationFeeBps()` |
| Most asks one buy fills | 64 | `Exchange.MAX_FILLS()` |
| Smallest ask | 5.00 OBOL | `Exchange.MIN_ORDER()` |
| Price grid | 2.5% steps, 2.5% to 97.5% off | `PRICE_STEP()`, `MIN_PRICE()`, `MAX_PRICE()` |
| Request body | 4.5 MB at most | larger inputs: openrouter.ai/api/v1 with the same key |
| Streaming response | 300 s at most | longer generations: openrouter.ai/api/v1 |
| Keys per wallet | 5 in 24 hours | `429 too_many_keys` |

## Errors

Pontica's own errors share one body; errors from OpenRouter pass through unchanged.

```json
{ "error": { "message": "Plain sentence.", "type": "invalid_request_error", "code": "signature_expired", "param": "message" } }
```

| HTTP | Codes | What to do |
|---|---|---|
| 400 | `bad_json` `missing_field` `bad_address` `bad_message` `wrong_chain` `wrong_domain` `address_mismatch` `signature_expired` `signature_from_future` `unsupported_parameter` | Fix the request or sign a fresh message. |
| 401 | `missing_key` `bad_signature` `not_a_pontica_key` `key_retired` | Use the current key, or issue a new one. |
| 402 | `nothing_activated` | Activate OBOL to this wallet first. |
| 404 | `no_key` `not_found` | This wallet has no key yet. |
| 409 | `signature_used` `issue_conflict` | Another key request crossed this one. Sign again. |
| 413 | `body_too_large` | Send large inputs to openrouter.ai/api/v1 with the same key. |
| 429 | `too_many_keys` | Five keys in 24 hours. Try again later. |
| 502 | `upstream_unavailable` `chain_unavailable` | The gateway could not reach OpenRouter or Robinhood Chain. Try again. |
| 503 | `gateway_not_connected` `gateway_key_expired` | The inference gateway is not connected. Keys and chat open when it is. |

## Contracts

Robinhood Chain, chain id 4663. Explorer: https://robinhoodchain.blockscout.com. The Pontica contracts were deployed at block 69,584,307 on 22 Sep 2026 and are verified on Sourcify.

| Contract | Address | Role | ABI |
|---|---|---|---|
| OBOL | `0x66F468093809C0a7fD7F6DF21Daf8d4f13D9e763` | The credit token (Pontica Obol, 6 decimals). `activate` burns it into an API balance; `creditedOf` is that balance. | [obol.json](https://pontica.gg/protocol/abi/obol.json) |
| PonticaMint | `0xc9341ff123F1FfF917EbDf4a35cE76c208526145` | Mints OBOL at $1 against USDG, or ETH swapped on the WETH/USDG 0.01% pool; the USDG goes to the treasury. | [mint.json](https://pontica.gg/protocol/abi/mint.json) |
| PonticaExchange | `0x30966731d86752Becef4356206E0c56103592F9B` | The discount book: `sell`, `cancel`, `claim`, `getBestQuote`, `buyWith`, `buyAndActivateWith`. | [exchange.json](https://pontica.gg/protocol/abi/exchange.json) |
| PonticaStaking | `0x449bD820007be99822D0113C8B064053f9cFa4F3` | Stake $PONTICA, earn OBOL streamed over 7 days. | [staking.json](https://pontica.gg/protocol/abi/staking.json) |
| PonticaFunds | `0xa4f136795AD5AbC757c2e9a0B5634791C5e2050a` | AI Funding: goals, deadlines, refunds and daily draws in OBOL. | [funds.json](https://pontica.gg/protocol/abi/funds.json) |
| USDG | `0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168` | Global Dollar, 6 decimals. What OBOL is bought and minted with. | [erc20.json](https://pontica.gg/protocol/abi/erc20.json) |
| WETH | `0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73` | Wrapped ether on Robinhood Chain. | [erc20.json](https://pontica.gg/protocol/abi/erc20.json) |
| SwapRouter02 | `0xCaf681a66D020601342297493863E78C959E5cb2` | The Uniswap router the Mint swaps ETH through (`exactInputSingle`). | standard |
| WETH/USDG pool | `0x52e65B17fB6E5BA00Ed806f37Afcd2DaA50271Ca` | Uniswap v3 pool, 0.01% fee tier. `fundETH` swaps here. | standard |
| Multicall3 | `0xcA11bde05977b3631167028862bE2a173976CA11` | Batch reads (`aggregate3`). | standard |
