Skip to content

Commit 4a2f911

Browse files
jarrodwattsclaude
andcommitted
Add playing-lingo skill for Wordle-style ETH duel game on Abstract
Adds skill with MCP tool reference, game rules, and SignedVault contract docs for practice mode, PvP duels, deposits, withdrawals, and jackpots. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 55e9873 commit 4a2f911

5 files changed

Lines changed: 644 additions & 0 deletions

File tree

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
---
2+
name: playing-lingo
3+
description: Play the Lingo word guessing game on Abstract chain — practice mode, ETH duel betting, stickers, achievements, referrals, and jackpots via MCP tools. Use when a user wants to play Lingo, start a practice game, create or join a duel, guess a word, check match status, deposit or withdraw ETH for duels, view stats or leaderboard, collect stickers, claim achievements, do daily check-in, use referral codes, or check the jackpot. Trigger for requests mentioning Lingo, Wordle duel, word game, practice game, duel bet, guess word, match status, ETH deposit for game, withdraw winnings, player stats, leaderboard, stickers, achievements, check-in, referral, or jackpot.
4+
---
5+
6+
# Playing Lingo
7+
8+
Lingo is a competitive Wordle-style word guessing game on the Abstract blockchain. Players guess 5-letter words,
9+
receive per-tile feedback (green/yellow/gray), and compete in practice mode (free, vs bot) or ETH-betting duels (1v1
10+
PvP). The game features a jackpot system, LP-based ranking, achievements, stickers, daily check-ins, and referrals.
11+
12+
## Operating Rules
13+
14+
- **Authentication required.** Most MCP tools require a Bearer token. Generate one at
15+
[https://witty.game/lingo/mcp-token](https://witty.game/lingo/mcp-token) using the same wallet your agent uses, then
16+
add the MCP server to your client.
17+
- **Start guessing immediately.** After creating a duel, submit guesses right away — even if the match status is
18+
"waiting" (no opponent yet). Do NOT wait for an opponent to join.
19+
- **Deposit before dueling.** You must complete an on-chain `depositETH` transaction to the SignedVault contract BEFORE
20+
calling `lingo_duel_create`. Read [references/signed-vault-contract.md](./references/signed-vault-contract.md) for
21+
the full deposit flow.
22+
- **Withdraw after winning.** When you win a duel, the match result contains a `withdrawal_signature`. Use it to call
23+
`withdrawETH` on-chain to claim your ETH. Jackpot wins require a separate withdrawal.
24+
- **Preview on-chain transactions.** Always use `--dry-run` before `--execute` for any contract interaction.
25+
- Read [references/mcp-tools.md](./references/mcp-tools.md) for the complete tool reference.
26+
- Read [references/game-rules.md](./references/game-rules.md) for detailed game rules, tiers, and economy.
27+
28+
## MCP Setup
29+
30+
### 0. Set up your agent wallet
31+
32+
Your agent needs access to a wallet on Abstract chain to deposit ETH and play duels. If you don't have one yet, the
33+
[AGW CLI](https://github.com/Abstract-Foundation/agw-cli) is one easy way to set up a wallet your agent uses.
34+
35+
### 1. Generate a token
36+
37+
Visit [https://witty.game/lingo/mcp-token](https://witty.game/lingo/mcp-token) and generate an MCP access token.
38+
39+
**Important:** You must log in with the **same wallet your agent uses** (the AGW wallet). If the token is generated from
40+
a different wallet, deposits, withdrawals, and duel operations will fail because the on-chain wallet won't match the
41+
authenticated player account.
42+
43+
### 2. Add the MCP server
44+
45+
**Claude Code (CLI / Desktop):**
46+
47+
```bash
48+
claude mcp add --transport http --scope user lingo https://api.lingo.witty.game/mcp --header "Authorization: Bearer <YOUR_TOKEN>"
49+
```
50+
51+
**Claude Desktop (manual config — `claude_desktop_config.json`):**
52+
53+
```json
54+
{
55+
"mcpServers": {
56+
"lingo": {
57+
"url": "https://api.lingo.witty.game/mcp",
58+
"headers": {
59+
"Authorization": "Bearer <YOUR_TOKEN>"
60+
}
61+
}
62+
}
63+
}
64+
```
65+
66+
**Cursor / other MCP clients:**
67+
68+
| Property | Value |
69+
|----------|-----------------------------------------------|
70+
| URL | `https://api.lingo.witty.game/mcp` |
71+
| Method | `POST` |
72+
| Header | `Authorization: Bearer <YOUR_TOKEN>` |
73+
74+
Replace `<YOUR_TOKEN>` with the token from step 1.
75+
76+
## MCP Server
77+
78+
| Property | Value |
79+
|----------|-----------------------------------------------|
80+
| Endpoint | `https://api.lingo.witty.game/mcp` |
81+
| Protocol | MCP (Model Context Protocol) |
82+
| Auth | `Authorization: Bearer <accessToken>` |
83+
84+
The MCP server exposes tools (actions) and resources (read-only data):
85+
86+
**Resources** (read via MCP `resources/read`):
87+
- `lingo://rules/game` — game rules, mechanics, winner determination
88+
- `lingo://rules/tiers` — betting tier configuration and fee breakdown
89+
- `lingo://rules/ranks` — LP rank thresholds (Bronze/Silver/Gold)
90+
- `lingo://contracts/chain-info` — Abstract chain ID, RPC, explorer
91+
- `lingo://contracts/signed-vault` — SignedVault contract address, resolver, full ABI
92+
- `lingo://contracts/deposit-guide` — step-by-step ETH deposit instructions
93+
- `lingo://contracts/withdraw-guide` — step-by-step ETH withdrawal instructions
94+
- `lingo://contracts/subgraph` — subgraph URL, schema, example GraphQL queries
95+
96+
## Betting Tiers
97+
98+
| Tier | Bet | Winner Gets | Gem Reward |
99+
|---------|-----------|-------------|------------|
100+
| casual | 0.001 ETH | 0.0019 ETH | 10 gems |
101+
| shrimp | 0.01 ETH | 0.019 ETH | 50 gems |
102+
| whale | 0.1 ETH | 0.19 ETH | 250 gems |
103+
104+
Fee: 5% total (2.5% jackpot + 2.5% protocol). Both players earn gems regardless of outcome.
105+
106+
## Contract
107+
108+
| Property | Value |
109+
|--------------------|----------------------------------------------|
110+
| SignedVault | `0xF5005cCA582Cb510D15d4D025F78C9258ec07F4b` |
111+
| Network | Abstract Mainnet (chain ID 2741) |
112+
| Resolver Address | `0xde27F91F4A1CA98AfD519315432424b7d0346e3C` |
113+
| ETH Token Address | `0x0000000000000000000000000000000000000000` (native ETH sentinel) |
114+
| Subgraph URL | `https://api.goldsky.com/api/public/project_cmgz8pxdm000i5ep21i0oazas/subgraphs/signed-vault-subgraph-abstract/latest/gn` |
115+
| Lingo App ID | `223` (for Abstract Portal voting) |
116+
117+
These addresses and the full ABI are also available at runtime via the MCP resource `lingo://contracts/signed-vault`.
118+
The subgraph URL and example queries are available via `lingo://contracts/subgraph`.
119+
120+
## ABI Format
121+
122+
The AGW CLI requires full JSON ABI objects, not human-readable strings. Every `abi` array element must be an object with
123+
`type`, `name`, `inputs`, `outputs`, and `stateMutability` fields.
124+
125+
## Task Map
126+
127+
### Play a practice game (free)
128+
129+
```
130+
1. Call lingo_practice_start → returns session_id and word length
131+
2. Call lingo_practice_guess with session_id and a 5-letter word
132+
3. Read the feedback: green (right letter, right spot), yellow (right letter, wrong spot), gray (not in word)
133+
4. Repeat guesses (up to 6 total) using feedback to narrow down the answer
134+
5. The bot solves on turn 4 — try to solve in 3 or fewer turns to win
135+
```
136+
137+
### Play a duel (ETH betting)
138+
139+
```
140+
1. Choose a tier: casual (0.001 ETH), shrimp (0.01 ETH), whale (0.1 ETH)
141+
2. Deposit ETH on-chain:
142+
- Pick a random unused nonce (e.g., current unix timestamp)
143+
- Call SignedVault.depositETH(resolver, nonce) with value = tier bet amount
144+
- Wait for confirmation
145+
3. Call lingo_duel_create with tier and deposit_nonce
146+
- For private rooms: set is_private=true (returns invite_code)
147+
- To join a private room: pass invite_code
148+
4. Start guessing immediately with lingo_duel_guess — do NOT wait for an opponent
149+
5. Use tile feedback to solve the word in as few turns as possible
150+
6. When the match completes, call lingo_duel_status to get the result
151+
7. If you won:
152+
- Call SignedVault.withdrawETH using withdrawal_signature from the match result
153+
- If you solved on turn 1 (jackpot!), make a separate withdrawETH call with the jackpot fields
154+
```
155+
156+
### Check match status
157+
158+
```
159+
Call lingo_duel_status with match_id
160+
- Opponent guesses are hidden until you finish your turns
161+
- Answer words are hidden until the match completes
162+
- Winner gets withdrawal_signature, withdrawal_nonce, withdrawal_deadline in the response
163+
```
164+
165+
### Scan history and auto-withdraw unclaimed winnings
166+
167+
When browsing duel history (`lingo_duel_history`), proactively check for completed matches where the player won
168+
but hasn't withdrawn on-chain yet. Withdraw any unclaimed winnings on the spot.
169+
170+
```
171+
1. Call lingo_duel_history (paginate through all pages if needed)
172+
2. For each match where is_winner == true and withdrawal_signature exists:
173+
a. Extract the nonce from withdrawal_signature.nonce
174+
b. Call SignedVault.usedNonces(resolver, nonce) on-chain to check if already withdrawn:
175+
agw contract write --json '{
176+
"address": "0xF5005cCA582Cb510D15d4D025F78C9258ec07F4b",
177+
"abi": [{"type":"function","name":"usedNonces","stateMutability":"view","inputs":[{"name":"resolver","type":"address"},{"name":"nonce","type":"uint256"}],"outputs":[{"name":"used","type":"bool"}]}],
178+
"functionName": "usedNonces",
179+
"args": ["0xde27F91F4A1CA98AfD519315432424b7d0346e3C", "<NONCE>"]
180+
}' --dry-run
181+
c. If usedNonces returns false → the withdrawal is unclaimed. Execute withdrawETH:
182+
agw contract write --json '{
183+
"address": "0xF5005cCA582Cb510D15d4D025F78C9258ec07F4b",
184+
"abi": [{"type":"function","name":"withdrawETH","stateMutability":"nonpayable","inputs":[{"name":"user","type":"address"},{"name":"amount","type":"uint256"},{"name":"resolver","type":"address"},{"name":"nonce","type":"uint256"},{"name":"deadline","type":"uint256"},{"name":"signature","type":"bytes"}],"outputs":[]}],
185+
"functionName": "withdrawETH",
186+
"args": ["<USER>", "<AMOUNT>", "0xde27F91F4A1CA98AfD519315432424b7d0346e3C", "<NONCE>", "<DEADLINE>", "<SIGNATURE>"]
187+
}' --dry-run
188+
All values come directly from the withdrawal_signature object in the match result.
189+
d. Same check for jackpot_withdrawal_signature if jackpot_amount is present.
190+
3. Report which matches were already claimed and which were newly withdrawn.
191+
```
192+
193+
**Tip:** This is useful as a periodic sweep — winnings don't expire (deadline is ~100 years), so unclaimed
194+
withdrawals stay valid indefinitely, but it's better to claim them sooner.
195+
196+
### Join a waiting public match
197+
198+
```
199+
1. Call lingo_duel_waiting with the tier to see available matches
200+
2. Deposit ETH on-chain for the chosen tier
201+
3. Call lingo_duel_create with the tier — matchmaking pairs you with a waiting opponent
202+
```
203+
204+
### Deposit ETH on-chain (before dueling)
205+
206+
```bash
207+
agw contract write --json '{
208+
"address": "0xF5005cCA582Cb510D15d4D025F78C9258ec07F4b",
209+
"abi": [{"type":"function","name":"depositETH","stateMutability":"payable","inputs":[{"name":"resolver","type":"address"},{"name":"nonce","type":"uint256"}],"outputs":[]}],
210+
"functionName": "depositETH",
211+
"args": ["0xde27F91F4A1CA98AfD519315432424b7d0346e3C", "<NONCE>"],
212+
"value": "<BET_AMOUNT_WEI>"
213+
}' --dry-run
214+
```
215+
216+
Replace:
217+
- `<NONCE>` — a random unused uint256 (e.g., current unix timestamp)
218+
- `<BET_AMOUNT_WEI>` — casual: `1000000000000000`, shrimp: `10000000000000000`, whale: `100000000000000000`
219+
220+
Execute after confirming the preview: replace `--dry-run` with `--execute`.
221+
222+
### Withdraw ETH winnings (after winning a duel)
223+
224+
```bash
225+
agw contract write --json '{
226+
"address": "0xF5005cCA582Cb510D15d4D025F78C9258ec07F4b",
227+
"abi": [{"type":"function","name":"withdrawETH","stateMutability":"nonpayable","inputs":[{"name":"user","type":"address"},{"name":"amount","type":"uint256"},{"name":"resolver","type":"address"},{"name":"nonce","type":"uint256"},{"name":"deadline","type":"uint256"},{"name":"signature","type":"bytes"}],"outputs":[]}],
228+
"functionName": "withdrawETH",
229+
"args": ["<YOUR_ADDRESS>", "<WINNER_REWARD_WEI>", "0xde27F91F4A1CA98AfD519315432424b7d0346e3C", "<WITHDRAWAL_NONCE>", "<WITHDRAWAL_DEADLINE>", "<WITHDRAWAL_SIGNATURE>"]
230+
}' --dry-run
231+
```
232+
233+
All values (`withdrawal_nonce`, `withdrawal_deadline`, `withdrawal_signature`, reward amount) come from the
234+
`lingo_duel_status` response after winning.
235+
236+
### Verify a deposit on-chain
237+
238+
```bash
239+
agw contract write --json '{
240+
"address": "0xF5005cCA582Cb510D15d4D025F78C9258ec07F4b",
241+
"abi": [{"type":"function","name":"getDeposit","stateMutability":"view","inputs":[{"name":"user","type":"address"},{"name":"token","type":"address"},{"name":"resolver","type":"address"},{"name":"nonce","type":"uint256"}],"outputs":[{"name":"amount","type":"uint256"}]}],
242+
"functionName": "getDeposit",
243+
"args": ["<YOUR_ADDRESS>", "0x0000000000000000000000000000000000000000", "0xde27F91F4A1CA98AfD519315432424b7d0346e3C", "<NONCE>"]
244+
}' --dry-run
245+
```
246+
247+
Use `0x0000000000000000000000000000000000000000` as the token address for ETH deposits.
248+
249+
### Daily check-in
250+
251+
```
252+
Call lingo_checkin with today's date (YYYY-MM-DD format)
253+
Streak bonus: 1d=10, 2d=20, 3d=30, 4d=40, 5d+=50 gems. Missing a day resets streak.
254+
```
255+
256+
### View stats and leaderboard
257+
258+
```
259+
- lingo_player_stats — your full stats (wins, losses, ETH, LP, rank, gems, streaks)
260+
- lingo_player_profile — your or another player's profile with recent matches
261+
- lingo_leaderboard — LP rankings (no auth required)
262+
```
263+
264+
### Collect and manage stickers
265+
266+
```
267+
- lingo_purchase_sticker_pack — buy a random sticker for 200 gems
268+
- lingo_my_stickers — view owned stickers
269+
- lingo_upgrade_sticker — combine duplicates to upgrade level
270+
- lingo_set_pfp — set a sticker as profile picture
271+
```
272+
273+
### Claim achievements
274+
275+
```
276+
1. Call lingo_achievements to see all achievements and unlock status
277+
2. Call lingo_claim_achievement with the key (e.g., "first_win") to claim gems
278+
```
279+
280+
### Use referral codes
281+
282+
```
283+
- lingo_apply_referral — apply someone's referral code (one-time)
284+
- lingo_referral_dashboard — view your referral earnings
285+
- lingo_referral_withdraw — withdraw accumulated referral earnings
286+
```
287+
288+
### Check jackpot
289+
290+
```
291+
- lingo_jackpot_pool — current jackpot amount (no auth)
292+
- lingo_jackpot_history — past jackpot winners (no auth)
293+
The jackpot is won by solving a duel word on turn 1.
294+
```
295+
296+
### Claim upvote reward
297+
298+
```
299+
After voting for Lingo on the Abstract Portal (via the upvoting-on-abstract skill),
300+
call lingo_upvote_claim with the epoch number to receive 50 gems.
301+
```
302+
303+
## Wordle Strategy Tips
304+
305+
- **Start with vowel-rich words** like CRANE, SLATE, AUDIO, RAISE to maximize information.
306+
- **Use feedback aggressively**: eliminate gray letters, lock green letters, reposition yellow letters.
307+
- **Green tiles matter even after solving**: in duels, tiebreakers count total green tiles across all guesses. So even
308+
if you know the answer, consider the quality of your earlier guesses.
309+
- **Turn 1 solve wins the jackpot**: if you're feeling lucky, guess a common word on turn 1 in a duel.
310+
311+
## Error Handling
312+
313+
| Error Message | Cause | Fix |
314+
|---------------------------------|----------------------------------------------|-------------------------------------------------|
315+
| `Login required` | Missing or invalid Authorization header | Add `Authorization: Bearer <token>` to headers |
316+
| `Player is busy` | Player row locked by concurrent request | Retry after a short delay |
317+
| `Player not found` | Invalid player ID or token | Re-authenticate |
318+
| `Match not found` | Invalid match_id | Check match_id from create/history response |
319+
| `Match already completed` | Trying to guess on a finished match | Check status and start a new match |
320+
| `Invalid word` | Word not in dictionary | Use a valid 5-letter English word |
321+
| `Already guessed this word` | Duplicate guess in same session | Try a different word |
322+
| `Maximum guesses reached` | Used all 6 guesses | Game over — check result |
323+
| `Deposit not found` | Nonce not found on-chain | Verify deposit transaction confirmed |
324+
| `Already checked in today` | Duplicate daily check-in | Wait until tomorrow |
325+
326+
## Escalation
327+
328+
- Route on-chain deposit/withdrawal to `executing-agw-transactions`.
329+
- Route wallet balance checks to `reading-agw-wallet`.
330+
- Route Abstract Portal voting to `upvoting-on-abstract`.
331+
- Route app discovery to `discovering-abstract-portal`.
332+
- Route AGW session setup to `authenticating-with-agw`.

0 commit comments

Comments
 (0)