---
title: "BASE167 Plugin"
description: "Claim BASE167, enter Degen Games, and mint Entry and Winner NFTs on Base."
tags: [airdrop, claim, nft, competition, memecoin]
name: base167
version: 0.1.0
integration: http-only
chains: [base]
requires:
  shell: optional
  allowlist: [base167.com]
  externalMcp: null
  cliPackage: null
auth: none
risk: [irreversible-onchain]
---

# BASE167 Plugin

> [!IMPORTANT]
> Complete the short Base MCP onboarding flow defined in `SKILL.md` before calling any
> BASE167 flow.

## Overview

BASE167 is a meme coin on Base that cannot be bought, only claimed, and only by wallets
with 167 or more transactions on Base mainnet. Holders also get free entry to Degen
Games, a monthly on-chain trading competition ranked by percentage gain rather than
wallet size.

This is an **HTTP-only plugin**. Every endpoint is a plain JSON POST over HTTPS and each
one returns ready-to-send calldata, so no CLI, SDK or additional MCP server is needed.
The service never signs a user transaction and never holds keys: it issues a short-lived
voucher, and the user approves the transaction in Base Account through `send_calls`.

> [!NOTE]
> BASE167 also runs its own remote MCP server at `https://base167.com/mcp`, exposing these
> same actions as seven named tools with no key and no OAuth. This plugin remains the right
> route inside Base MCP, where the assistant already holds the Base Account and `send_calls`;
> the standalone server is for assistants reaching BASE167 directly.

**Chain:** Base mainnet only (`chainId` 8453, Base MCP chain string `"base"`).

## Installation

None. The endpoints are public and unauthenticated. Allowlist `base167.com`.

## What a season pays

| Level | Pot |
| --- | --- |
| Monthly season, 1st | 150,000,000 BASE167 |
| Monthly season, 2nd | 100,000,000 BASE167 |
| Monthly season, 3rd | 50,000,000 BASE167 |
| Monthly season total | 300,000,000 BASE167 |
| Annual Finals | 1,200,000,000 BASE167 |

Only the **podium** is paid. Ten places count as winners -- everyone in the top ten mints a
rank-stamped Winner badge NFT and takes a Finals seat -- but money stops at three. Read
`winnersCount` from `/api/games/state` rather than assuming ten. Always state these as
BASE167 amounts, never as a cash value: there is no reliable price to convert at.

## Who can enter, and when

Two gates, in order:

1. **The holder snapshot** at `snapshotAt` records the top BASE167 holders. Only an address
   in it can claim that season's Entry pass. The list size is the `topHolders` config value.
2. **The enrolment window**, `enrollOpenAt` to `enrollCloseAt`, is when a pass holder
   registers a competition wallet. That is the act that enters them.

Enrolling **locks** the Entry pass until the closing bell, after which
`EntryNFT.releaseLock(tokenId)` is permissionless. Holding several passes makes a user a
Collector, which is what the Finals seats (`finalsCollectorSeats`) are drawn from.

**Never hard-code a date.** Read the five timestamps -- `snapshotAt`, `enrollOpenAt`,
`enrollCloseAt`, `openingBellAt`, `closingBellAt` -- from `GET /api/games/state`, all unix
seconds, and report `phase` with `nextBoundary` rather than computing the season's state
yourself. Seasons get rescheduled. `phase` is one of `pre`, `seatMarket` (passes claimable
and tradeable), `setup` (enrolment window), `preGame` (enrolment closed, awaiting the bell),
`game` (trading) or `settled` (closing bell passed).

## Read endpoints

| What | Call |
| --- | --- |
| Season, phase, config, boards | `GET https://base167.com/api/games/state` |
| Wallet eligibility and tx count | `POST https://base167.com/api/check` with `{ "address": "0x..." }` |
| Top holders and collector board | `GET https://base167.com/api/games/leaderboard` |
| Published finals for past seasons | `GET https://base167.com/api/games/results` |
| Full API schema | `GET https://base167.com/openapi.json` |

**Never cache claim counts, remaining supply or phase.** They change constantly. Read
`/api/games/state` or the token's `claimCount()` at the moment you need them.

## Prepare endpoints

Each returns `call: { chainId, to, data, value }` alongside the voucher. Map that object
straight into the `calls` array of `send_calls`.

| Flow | Endpoint | Body |
| --- | --- | --- |
| Claim BASE167 | `POST /api/check` | `{ "address": "<wallet>" }` |
| Claim the Entry pass | `POST /api/games/claim-entry` | `{ "address": "<wallet>" }` |
| Register a competition wallet | `POST /api/games/enroll` | `{ "player": "<wallet>", "gameWallet": "<competition wallet>" }` |
| Claim a Winner badge | `POST /api/games/claim-winner` | `{ "address": "<wallet>" }` |

## Sign-only endpoints (no transaction)

Setting a display name or an X handle stores a label on the BASE167 server. There is no
`call` in the response and nothing to send through `send_calls` -- do not go looking for
one. These are the only two flows where the **user's wallet** produces a signature; every
other flow is signed by the BASE167 server.

| Flow | Endpoint | Body |
| --- | --- | --- |
| Set display name | `POST /api/games/set-name` | `{ "address", "name", "signature", "expiry" }` |
| Set X handle | `POST /api/games/set-handle` | `{ "address", "handle", "signature", "expiry" }` |

Ask the wallet to sign this exact string as a personal_sign / EIP-191 message. It must
byte-match the server or the answer is `401`:

```
Set my Degen Games name to: {name}
Domain: base167.com Degen Games
Expires: {expiry}
```

The handle flow uses a different prefix on purpose, so a signature captured for one
action can never be replayed as the other:

```
Set my Degen Games X handle to: {handle}
Domain: base167.com Degen Games
Expires: {expiry}
```

Build the message yourself rather than echoing one the user supplies. `{name}` and
`{handle}` are trimmed of surrounding whitespace before signing, and an empty value
clears the label. `{expiry}` is unix seconds, must be in the future, and may be at most
900 seconds ahead -- that bound is what limits the replay window. There is no trailing
newline. Send the same `expiry` in the JSON body as the one inside the signed string; a
mismatch is the most common cause of a `401` here.

## send_calls mapping

```json
{
  "chainId": "base",
  "calls": [{ "to": "<call.to>", "data": "<call.data>", "value": "<call.value>" }]
}
```

Send from **the same wallet the voucher was issued for**. Every contract binds
`msg.sender` into the signed preimage, so a transaction sent from a different wallet
reverts. Vouchers expire: if `expiry` has passed, request a fresh one rather than
retrying the old call.

## Rules the assistant must respect

1. **The competition wallet is judged at the bell, not at enrolment.** Any wallet may be
   enrolled, including a Base Account or any smart account, and prior history is fine.
   Before the opening bell it must hold at least $5 USDC, no more than 0.0002 ETH of gas,
   and nothing else of value. More ETH than the allowance is a disqualification, and
   non-USDC holdings are flagged for review -- scoring counts USDC only, so an unscored
   asset sold during the season would read as profit.
2. **Prizes have no claim function.** `claimWinner` mints a badge NFT and moves no
   value. Season payouts are made manually. Do not tell a user their prize can be
   claimed on chain, and do not go looking for a method.
3. **Naming works for smart-contract wallets** -- ERC-1271, ERC-6492 and ERC-8010 are all
   verified. A `503` from a naming route means a chain could not be reached, not that
   the signature was bad. Retry.
4. **Respect the rate limit**: 5 requests per minute per IP on the POST routes, `429` with
   `Retry-After`. The GET reads are not limited.
5. **A `409` is usually a closed window**, not a fault. Enrolment opens and closes on a
   schedule; report the phase from `/api/games/state` instead of retrying.

## Contracts

| What | Address |
| --- | --- |
| BASE167 token | `0x2Bf0234d7E9C25687521D2d2192d1dAE70aA4f2B` |
| SeasonFactory | `0x8Cf6ae46C03801D8Ae7Fbdc643AF34003E83b345` |
| EntryNFT (season 1) | `0x6e403A853CBC6962f29E0C766e4fDD611A2a769b` |
| WinnersNFT (season 1) | `0xA7762135805D3b7E9CA6F9a990d99A861E8b4a41` |

After a season ends, `EntryNFT.releaseLock(tokenId)` is permissionless and needs no
voucher.

## Risk notes

Every action here is an irreversible on-chain transaction on Base mainnet. Claiming is
one-shot per wallet: `AlreadyClaimed` reverts a second attempt. Earlier claimers receive
more on a decay curve, so the amount falls with every claim and cannot be quoted ahead
of time.
