# retard.market V9 — agent instructions

This is the machine-facing runbook for the live retard.market V9 contracts. It
covers discovery, market launch, buying, selling, settlement, winner claims,
creator-proceeds claims, and referral-fee claims.

Treat every address, duration, minimum seed, fee, and market state as live
data. Read it again immediately before signing. Do not copy an address from an
old transaction, cached page, screenshot, or this document.

Nothing here is financial advice. A creator's seed is a real position in the
game and can lose value. A winning answer token does not have a fixed $1
redemption value.

## Network and discovery

- Application: `https://vibechain.com/retard`
- Chain: Robinhood Chain mainnet, chain ID `4663`
- Native currency: ETH, 18 decimals
- Public RPC: `https://rpc.mainnet.chain.robinhood.com`
- Explorer: `https://robinhoodchain.blockscout.com`
- Runtime: `GET https://vibechain.com/api/retard/v9/runtime`
- Health: `GET https://vibechain.com/api/retard/v9/health`
- Markets: `GET https://vibechain.com/api/retard/v9/markets?cursor=0&limit=50`
- One market: `GET https://vibechain.com/api/retard/v9/markets/{market}`
- Trade tape: `GET https://vibechain.com/api/retard/v9/markets/{market}/trades`
- Wallet portfolio: `GET https://vibechain.com/api/retard/v9/portfolio/{wallet}?cursor=0&limit=50`

## API key

Every retard.market HTTP API request requires a VibeChain API key in the
`API-KEY` header. Create a free caller-specific key before reading runtime:

```bash
curl -X POST https://build.vibechain.com/apikey/create \
  -H "Content-Type: application/json" \
  -d '{"description":"YOUR_AGENT_NAME - retard.market","email":"YOUR_EMAIL"}'
```

Store the returned key outside source control and send it on every request:

```bash
curl https://build.vibechain.com/api/retard/v9/runtime \
  -H "API-KEY: YOUR_API_KEY"
```

The key identifies the caller for quota and abuse control; it does not replace
wallet signatures or onchain simulation. Do not copy the retard.market web
application's first-party key. Do not put your own key in a URL. SSE clients
must use a client capable of attaching the `API-KEY` header.

Start every session by reading `runtime`. Stop if `enabled` or `publicEnabled`
is false. Only `runtime.launchers` may be used for a new launch. That array is
the source of truth for the currently supported durations, launcher addresses,
deployment blocks, and minimum seeds. Disabled historical launchers are
deliberately absent.

Before using a launcher, attest its onchain values against the runtime result:

```solidity
function EPOCH_DURATION() view returns (uint40);
function MINIMUM_TOTAL_SEED() view returns (uint256);
function MARKET_IMPLEMENTATION() view returns (address);
function WETH() view returns (address);
function UNISWAP_FACTORY() view returns (address);
```

Require the wallet's chain ID to equal `runtime.chainId`. Use integer wei for
all ETH/WETH values. Wait for a successful receipt after every write.

## Contract states and sides

Market `state()` is:

| value | state | allowed agent action |
| --- | --- | --- |
| `1` | Open | buy or sell before `CLOSES_AT()` |
| `2` | Awaiting settlement | wait for or help execute settlement |
| `3` | Resolved | claim winning tokens and creator proceeds |

`yesSide = true` means `YES_TOKEN()` and `yesPool()`. `yesSide = false` means
`NO_TOKEN()` and `noPool()`.

Every answer is a normal fixed-supply ERC-20 paired with WETH in its own
official full-range Uniswap V3 1% pool. The launcher prefixes its symbol with
`r`; for example, the launch symbol `KIRK` becomes `rKIRK`. Read `YES_TOKEN()`,
`NO_TOKEN()`, `symbol()`, and the official pool addresses from the market. Do
not infer addresses or symbols.

## Launch a market

1. Read `runtime` and choose one entry from `runtime.launchers` by its
   `epochSeconds`.
2. Read and attest the selected launcher onchain.
3. Prepare:
   - `question`: printable ASCII, 1–280 bytes, no leading/trailing spaces.
   - answer names: printable ASCII, 1–64 bytes.
   - answer symbols: 1–16 ASCII letters/digits. The launcher uppercases them,
     rejects punctuation, and rejects equal symbols.
   - `mediaUrl`: optional printable ASCII, at most 2,048 bytes. Use `""` when
     there is no media.
   - `value`: at least that launcher's live `minimumTotalSeedWei`.
4. Simulate the exact transaction from the creator wallet.
5. Send one payable `launch` transaction to the selected launcher.
6. Wait for success and decode `MarketLaunched` from the receipt to obtain the
   market, answer tokens, pools, market ID, and close timestamp.
7. Confirm `isMarket(market)`, `launchedBy(market)`, `CREATOR()`, `FACTORY()`,
   and `CLAIM_ROUTER()` onchain before publishing the addresses.

Minimal launcher ABI:

```solidity
function launch(
  string question,
  string mediaUrl,
  string yesName,
  string yesSymbol,
  string noName,
  string noSymbol
) payable returns (address market, address yesToken, address noToken);

event MarketLaunched(
  uint256 indexed marketId,
  address indexed market,
  address indexed launchedBy,
  address yesToken,
  address noToken,
  address yesPool,
  address noPool,
  string question,
  uint256 totalSeed,
  uint40 closesAt,
  bool isOfficial
);
```

The launch ETH is split between the two house sides and seeded into the two
official pools. The creator is therefore the house player. The seed is not a
refundable deposit: settlement accounts for the house inventory and credits
the creator with the house payout that actually won.

Optional media upload:

```http
POST /api/retard/v9/media
API-KEY: YOUR_API_KEY
Content-Type: image/* or video/*
X-File-Name: URL-encoded filename

<raw file bytes, maximum 5 MiB>
```

Use the returned `mediaUrl` in `launch`. Uploading is offchain and must finish
before the launch transaction. A plain launch needs no upload.

## Verify a market before trading

Never trade an arbitrary address merely because it implements a matching ABI.
Require all of the following:

1. The market appears in the paginated V9 market API, or an indexed launcher
   reports `isMarket(market) == true`.
2. `state() == 1` and current chain time is strictly less than `CLOSES_AT()`.
3. `FACTORY()` is a known V9 launcher and `CLAIM_ROUTER()` contains code.
4. The router's `routerVersion()` is exactly
   `0.9.3-self-buy-sell-claim-router`.
5. The market's token and pool addresses match the API record.

For new V9 markets, fail closed if the unified router check fails. Do not fall
back to legacy approval-based trading.

## Buy YES or NO

The public quote endpoint provides a principal-only preview:

```text
GET /api/retard/v9/markets/{market}/quote?side=YES&amount={wei}
```

It is informative, not a signed or executable quote. Immediately before the
write, simulate the router call from the buying wallet with the exact `value`.
Use the simulation's `tokensOut` to calculate a nonzero slippage floor. The web
application uses 5% as its default:

```text
minTokensOut = simulatedTokensOut * 9500 / 10000
deadline = min(now + 20 minutes, CLOSES_AT() - 1)
```

Then send:

```solidity
// on CLAIM_ROUTER(); msg.sender receives the answer tokens
function buy(
  address market,
  bool yesSide,
  uint256 minTokensOut,
  address referral,
  uint40 deadline
) payable returns (uint256 tokensOut);
```

Set `value` to the exact native ETH input. Use the zero address when there is
no valid referral. Never use the trader, market, or forced recipient as a
referral. The current wrapper trade fee is exposed by `TRADE_FEE_BPS()` and is
currently zero; the separate official Uniswap V3 pool fee is exposed by
`POOL_FEE()` and is currently 10,000, or 1%.

The router forces both accounting and token delivery to `msg.sender`. It cannot
buy for an arbitrary wallet.

## Sell YES or NO

Read the selected answer token's `balanceOf(wallet)`. Token amounts use 18
decimals. Simulate this exact call from the selling wallet with
`minEthOut = 0`, then apply a slippage floor to the simulated ETH output:

```text
minEthOut = simulatedEthOut * 9500 / 10000
deadline = min(now + 20 minutes, CLOSES_AT() - 1)
```

Send:

```solidity
// on CLAIM_ROUTER(); ETH is forced back to msg.sender
function sell(
  address market,
  bool yesSide,
  uint256 tokensIn,
  uint256 minEthOut,
  address referral,
  uint40 deadline
) returns (uint256 ethOut);
```

The current unified flow requires **no ERC-20 approval**. The immutable market
can move only the calling holder's tokens through its immutable self-only
router, and the native ETH payout is forced to that holder. Do not call
`approve()` for a current V9 router sale.

Both buys and sells stop once the onchain market is no longer Open or chain
time reaches `CLOSES_AT()`. A transaction prepared before the deadline can
still revert if it lands after the close.

## Settlement

Closing and settlement are permissionless. A normal trading agent may wait for
the keeper; a keeper agent can perform the following sequence:

1. When `state() == 1` and chain time is at least `CLOSES_AT()`, call `close()`.
   This atomically removes both official LP positions, freezes each side's WETH
   backing, and normally schedules `settlementBlock = close block + 2`.
2. Wait until the current block number is strictly greater than
   `settlementBlock`, then call `settle()`.
3. If nobody settled while the target block hash was available and current
   block is more than `settlementBlock + BLOCKHASH_WINDOW()`, call
   `rearmSettlement()`, wait past the new target, then call `settle()`.

Do not try to bundle `close()` and `settle()` into one transaction. If neither
side has an external holder, `close()` resolves immediately without a roll.

The roll is weighted by `closingYesBackingWeth` versus
`closingNoBackingWeth`. On resolution, 7.5% of recovered WETH is the settlement
rake, split between the bank and creator. The remaining winner pot is shared
pro rata between external winning tokens and the winning house inventory.

Minimal settlement ABI:

```solidity
function state() view returns (uint8);
function CLOSES_AT() view returns (uint40);
function close() returns (uint256 targetBlock);
function settlementBlock() view returns (uint256);
function BLOCKHASH_WINDOW() view returns (uint256);
function settle();
function rearmSettlement() returns (uint256 newSettlementBlock);
function winnerToken() view returns (address);
function winnerPotWeth() view returns (uint256);
```

## Claim winning positions

Discovery:

1. Paginate `/api/retard/v9/portfolio/{wallet}` until `nextCursor` is null.
2. Select positions where `state == "RESOLVED"`, `winner == true`, the token
   balance is positive, and `estimatedClaimWeth > 0`.
3. Re-read each market's `state()`, `winnerToken()`, winner-token
   `balanceOf(wallet)`, and `CLAIM_ROUTER()` onchain.
4. Group markets by their immutable claim-router address.

For each router group, the wallet sends one transaction:

```solidity
function claimAll(address[] markets)
  returns (uint256 totalWethAmount, uint256 claimedMarketCount);
```

No token approval is needed. `claimAll` always claims for `msg.sender`; each
market burns that wallet's entire winning balance and sends native ETH directly
to the same wallet. It cannot claim another wallet or redirect the proceeds.

The router skips stale, already-claimed, losing, or otherwise reverting market
entries and emits `MarketSkipped`. It reverts with `NothingClaimed` if every
entry fails. Inspect `MarketClaimed` and `MarketSkipped` in the receipt instead
of assuming every supplied market paid.

Losing tokens have no redemption. `estimatedClaimWeth` is a projection; the
onchain receipt and ETH balance change are final.

## Claim creator proceeds

Creator proceeds are not winner-token claims and are not handled by
`claimAll`. Discover them separately:

1. Paginate the complete `/api/retard/v9/markets` book until `nextCursor` is
   null, including historical markets returned by the API.
2. Filter markets where `launchedBy` equals the wallet.
3. Onchain, require `CREATOR() == wallet`, `state() == 3`, and
   `creatorFeeWeth() > 0`.
4. From that creator wallet, send one transaction per payable market:

```solidity
function withdrawCreatorFees(address recipient)
  returns (uint256 ethAmount);
```

Use the creator wallet as `recipient` unless the user explicitly requests a
different nonzero address. Only `CREATOR()` may call this function. It clears
the market's creator credit before unwrapping WETH and sending native ETH.

`creatorFeeWeth` is the unified creator-proceeds credit. After settlement it
can include:

- 50% of collected WETH-denominated Uniswap V3 LP fees;
- 50% of the 7.5% settlement rake, normally 3.75% of recovered WETH; and
- the creator's winning house payout from the launch seed position.

It is therefore intentionally larger than a narrow “creator fee” number in
some markets. It is not guaranteed to equal the original seed. The house
position participates in the game and its payout depends on which side wins
and the terminal inventory.

There is currently no creator-proceeds batch router. A wallet with creator
proceeds in five markets signs five `withdrawCreatorFees` transactions. Never
claim the same market twice; a second call reverts with `NoPayout`.

## Claim referral fees

The current V9 wrapper trade fee is zero, so new wrapper trades normally create
no referral credit. For completeness, an existing nonzero credit can be read
and claimed per market:

```solidity
function referralCreditWeth(address referral) view returns (uint256);
function claimReferralFees(address recipient) returns (uint256 ethAmount);
```

Only the credited referral wallet can call. The function clears its credit and
sends native ETH to the chosen nonzero recipient. Do not call when the credit
is zero.

`withdrawProtocolFees` is bank-owner-only and is not a public agent action.

## Minimal unified router ABI

```solidity
function routerVersion() view returns (string);
function buy(address market, bool yesSide, uint256 minTokensOut, address referral, uint40 deadline)
  payable returns (uint256 tokensOut);
function sell(address market, bool yesSide, uint256 tokensIn, uint256 minEthOut, address referral, uint40 deadline)
  returns (uint256 ethOut);
function claimAll(address[] markets)
  returns (uint256 totalWethAmount, uint256 claimedMarketCount);

event BuyRouted(address indexed holder, address indexed market, uint256 ethIn, uint256 tokensOut);
event SellRouted(address indexed holder, address indexed market, uint256 tokensIn, uint256 ethOut);
event MarketClaimed(address indexed holder, address indexed market, uint256 wethAmount);
event MarketSkipped(address indexed holder, address indexed market, bytes4 reasonSelector);
```

## Transaction safety checklist

For every write:

1. Re-read chain ID, runtime, market state, close time, and immutable router.
2. Simulate the exact calldata, sender, and `value` against the latest block.
3. Use explicit, nonzero slippage bounds for buys and sells.
4. Display the target contract, method, native value, token amount, minimum
   output, deadline, and expected recipient to the wallet owner.
5. Never request a token approval for the current unified V9 flow.
6. Submit only after the wallet owner authorizes the transaction.
7. Wait for a successful receipt and decode the relevant events.
8. Refresh API and onchain state after confirmation. The API is an index; chain
   state is authoritative for signing and settlement.

When any attestation differs from this runbook, stop. The live runtime and
verified contracts win over cached instructions.
