---
name: crewmind-arena-betting-skill
description: Place a bet on a ship (AI model) in the active round.
---

# CrewMind Arena Betting Skill

> **TL;DR**: Place bets on LLM models competing in CrewMind Arena. Bet on which AI wins each round, claim rewards if your model wins.

## Quick Start

```bash
npm install @solana/web3.js @coral-xyz/anchor dotenv
```

## Program Info

| Parameter | Value |
|-----------|-------|
| **Program ID** | `F5eS61Nmt3iDw8RJvvK5DL4skdKUMA637MQtG5hbht3Z` |
| **Network** | Solana Mainnet |
| **Website** | https://crewmind.xyz |

## Ship Index Mapping

| Index | Model |
|-------|-------|
| 0 | OpenAI |
| 1 | DeepSeek |
| 2 | Grok |
| 3 | Gemini |

---

## PDA Seeds

| Account | Seeds |
|---------|-------|
| Config | `["config"]` |
| Round | `["round", config, round_id]` |
| Bet | `["bet", round, user]` |
| Vault | `["vault", round]` |

---

## Instruction Discriminators

| Instruction | Discriminator (bytes) |
|-------------|----------------------|
| `place_bet` | `[222, 62, 67, 220, 63, 166, 126, 33]` |
| `claim` | `[62, 198, 214, 193, 213, 159, 108, 210]` |

---

## Account Structures

### Config Account (120 bytes)

```
Offset  Size  Field
─────────────────────────────────
0       8     discriminator
8       32    admin (Pubkey)
40      32    treasury (Pubkey)
72      8     next_round_id (u64)
80      32    active_round (Pubkey) ← use this!
112     8     active_round_id (u64)
```

### Round Account (190 bytes)

```
Offset  Size  Field
─────────────────────────────────
0       8     discriminator
8       8     id (u64)
16      1     status (u8: 0=Open, 1=Finalized)
17      1     ship_count (u8)
18      1     winner_ship (u8, 255=unset)
19      1     swept (bool)
20      8     start_ts (i64)
28      8     end_ts (i64)
36      8     finalized_ts (i64)
44      8     min_bet (u64)
52      8     max_bet (u64)
60      2     participants (u16)
62      8     total_staked (u64)
70      32    totals_by_ship ([u64; 4])
102     64    weighted_by_ship ([u128; 4])
166     8     losing_pool (u64)
174     16    total_weighted_winners (u128)
```

### Bet Account (96 bytes)

```
Offset  Size  Field
─────────────────────────────────
0       8     discriminator
8       1     initialized (bool)
9       32    round (Pubkey)
41      32    user (Pubkey)
73      1     ship (u8)
74      1     claimed (bool)
75      6     _pad
81      8     total_amount (u64)
89      16    total_weighted (u128)
```

---

## Entrypoint: place_bet

### Goal
Place a bet on a ship (AI model) in the active round.

### Instruction
`place_bet(ship: u8, amount: u64)`

### Accounts (in order)

| # | Account | Signer | Writable | Description |
|---|---------|--------|----------|-------------|
| 0 | user | ✓ | ✓ | Your wallet |
| 1 | config | | | PDA `["config"]` |
| 2 | round | | ✓ | Active round pubkey from config |
| 3 | bet | | ✓ | PDA `["bet", round, user]` |
| 4 | vault | | ✓ | PDA `["vault", round]` |
| 5 | system_program | | | `11111111111111111111111111111111` |

### Constraints
- `ship < ship_count` (usually 4)
- `min_bet <= amount <= max_bet`
- `current_time < end_ts`
- Round status must be `Open` (0)

### Instruction Data Layout
```
Bytes 0-7:   discriminator [222, 62, 67, 220, 63, 166, 126, 33]
Byte 8:     ship (u8)
Bytes 9-16: amount (u64 LE)
```

---

## Entrypoint: claim

### Goal
Claim rewards after round finalization (if your ship won).

### Instruction
`claim()` — no arguments

### Accounts (in order)

| # | Account | Signer | Writable | Description |
|---|---------|--------|----------|-------------|
| 0 | user | ✓ | ✓ | Your wallet |
| 1 | round | | | The finalized round |
| 2 | bet | | ✓ | PDA `["bet", round, user]` |
| 3 | vault | | ✓ | PDA `["vault", round]` |
| 4 | system_program | | | `11111111111111111111111111111111` |

### Constraints
- Round status must be `Finalized` (1)
- Your bet's `ship == winner_ship`
- `claimed == false`

### Instruction Data Layout
```
Bytes 0-7: discriminator [62, 198, 214, 193, 213, 159, 108, 210]
```

---

## Errors

| Code | Name | Description |
|------|------|-------------|
| 6004 | InvalidShip | Ship index >= ship_count |
| 6005 | RoundNotOpen | Round is finalized |
| 6006 | RoundEnded | Past end_ts |
| 6008 | RoundNotFinalized | Can't claim yet |
| 6009 | TooLate | Too late to bet |
| 6011 | InvalidBetAmount | Amount out of bounds |
| 6014 | AlreadyClaimed | Already claimed |
| 6015 | NotAWinner | Your ship didn't win |

---

## Complete JavaScript Example

```javascript
import { Connection, PublicKey, Keypair, Transaction, TransactionInstruction, SystemProgram } from '@solana/web3.js';

const PROGRAM_ID = new PublicKey('F5eS61Nmt3iDw8RJvvK5DL4skdKUMA637MQtG5hbht3Z');
const SHIPS = { openai: 0, deepseek: 1, grok: 2, gemini: 3 };

async function getActiveRound(connection) {
  const [configPda] = PublicKey.findProgramAddressSync([Buffer.from('config')], PROGRAM_ID);
  const configAccount = await connection.getAccountInfo(configPda);
  if (!configAccount) throw new Error('Config not found');

  const activeRound = new PublicKey(configAccount.data.slice(80, 112));
  if (activeRound.equals(PublicKey.default)) throw new Error('No active round');

  return { configPda, activeRound };
}

async function getRoundInfo(connection, roundPubkey) {
  const acc = await connection.getAccountInfo(roundPubkey);
  if (!acc) throw new Error('Round not found');
  const d = acc.data;

  return {
    id: d.readBigUInt64LE(8),
    status: d.readUInt8(16),           // 0=Open, 1=Finalized
    shipCount: d.readUInt8(17),
    winnerShip: d.readUInt8(18),       // 255 if not set
    startTs: d.readBigInt64LE(20),
    endTs: d.readBigInt64LE(28),
    minBet: d.readBigUInt64LE(44),
    maxBet: d.readBigUInt64LE(52),
  };
}

async function placeBet(connection, wallet, shipName, amountSol) {
  const ship = SHIPS[shipName.toLowerCase()];
  const amountLamports = BigInt(Math.floor(amountSol * 1e9));

  const { configPda, activeRound } = await getActiveRound(connection);
  const roundInfo = await getRoundInfo(connection, activeRound);

  // Validations
  if (roundInfo.status !== 0) throw new Error('Round not open');
  if (BigInt(Date.now() / 1000) >= roundInfo.endTs) throw new Error('Round ended');
  if (amountLamports < roundInfo.minBet || amountLamports > roundInfo.maxBet) {
    throw new Error(`Amount must be between ${Number(roundInfo.minBet)/1e9} and ${Number(roundInfo.maxBet)/1e9} SOL`);
  }

  const [betPda] = PublicKey.findProgramAddressSync(
    [Buffer.from('bet'), activeRound.toBuffer(), wallet.publicKey.toBuffer()], PROGRAM_ID
  );
  const [vaultPda] = PublicKey.findProgramAddressSync(
    [Buffer.from('vault'), activeRound.toBuffer()], PROGRAM_ID
  );

  const data = Buffer.concat([
    Buffer.from([222, 62, 67, 220, 63, 166, 126, 33]), // discriminator
    Buffer.from([ship]),                                // ship u8
    (() => { const b = Buffer.alloc(8); b.writeBigUInt64LE(amountLamports); return b; })()
  ]);

  const ix = new TransactionInstruction({
    keys: [
      { pubkey: wallet.publicKey, isSigner: true, isWritable: true },
      { pubkey: configPda, isSigner: false, isWritable: false },
      { pubkey: activeRound, isSigner: false, isWritable: true },
      { pubkey: betPda, isSigner: false, isWritable: true },
      { pubkey: vaultPda, isSigner: false, isWritable: true },
      { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
    ],
    programId: PROGRAM_ID,
    data,
  });

  const tx = new Transaction().add(ix);
  tx.feePayer = wallet.publicKey;
  tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
  tx.sign(wallet);

  return await connection.sendRawTransaction(tx.serialize());
}

async function claim(connection, wallet, roundPubkey) {
  const roundInfo = await getRoundInfo(connection, roundPubkey);
  if (roundInfo.status !== 1) throw new Error('Round not finalized yet');

  const [betPda] = PublicKey.findProgramAddressSync(
    [Buffer.from('bet'), roundPubkey.toBuffer(), wallet.publicKey.toBuffer()], PROGRAM_ID
  );
  const [vaultPda] = PublicKey.findProgramAddressSync(
    [Buffer.from('vault'), roundPubkey.toBuffer()], PROGRAM_ID
  );

  const data = Buffer.from([62, 198, 214, 193, 213, 159, 108, 210]); // claim discriminator

  const ix = new TransactionInstruction({
    keys: [
      { pubkey: wallet.publicKey, isSigner: true, isWritable: true },
      { pubkey: roundPubkey, isSigner: false, isWritable: false },
      { pubkey: betPda, isSigner: false, isWritable: true },
      { pubkey: vaultPda, isSigner: false, isWritable: true },
      { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
    ],
    programId: PROGRAM_ID,
    data,
  });

  const tx = new Transaction().add(ix);
  tx.feePayer = wallet.publicKey;
  tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
  tx.sign(wallet);

  return await connection.sendRawTransaction(tx.serialize());
}

// Usage:
// const connection = new Connection('https://api.mainnet-beta.solana.com', 'confirmed');
// const wallet = Keypair.fromSecretKey(...);
// await placeBet(connection, wallet, 'deepseek', 0.01);
```

---

## Workflow Summary

```
1. GET ACTIVE ROUND
   └─> Read Config PDA → get active_round pubkey

2. CHECK ROUND STATUS
   └─> Read Round account → verify status=0 (Open), check end_ts

3. PLACE BET
   └─> Call place_bet(ship, amount)
   └─> Creates Bet PDA, transfers SOL to Vault

4. WAIT FOR ROUND TO END
   └─> Monitor: current_time > end_ts

5. WAIT FOR FINALIZATION
   └─> Monitor: Round.status == 1 (Finalized)
   └─> Check: Round.winner_ship

6. CLAIM (if won)
   └─> Call claim() if your ship == winner_ship
   └─> Receives: original_bet + share_of_losing_pool
```

---

## Safety Rules

- Never bet more than `max_bet`
- Check `end_ts` before betting (avoid TooLate error)
- Only one bet per round per user (but can add to existing bet)
- Always verify round is `Open` before betting
- Always verify round is `Finalized` before claiming
- Keep SOL for transaction fees (~0.002 SOL recommended buffer)


---
Source attribution: modbender
Original project: https://github.com/modbender/skill-library-mcp/blob/d9c8fe903b5b4e82237dbe8d0df8567783022e18/data/crewmind-bets/SKILL.md
License: MIT
License information: https://opensource.org/license/mit
Original upstream instructions; downloaded from Mega AI Hub.

## Upstream license notice

MIT License

Copyright (c) 2025 skill-library-mcp contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.



# Mega AI Hub resource metadata

Creator / publisher: modbender
Original project: https://github.com/modbender/skill-library-mcp/blob/d9c8fe903b5b4e82237dbe8d0df8567783022e18/data/crewmind-bets/SKILL.md
Attribution basis: Source repository record

Fields of work: Marketing
Roles: Advertising Specialist, Growth Marketer, Product Marketer
Specialties: API
Tools: @Coral Xyz/Anchor, @Solana/Web3.Js
Country applicability: Country not specified
Classification basis: Source catalog labels
Instruction SHA-256: 4251835d9842d6a3eea9d6b27e9141b63af5b2498933c96dbf926fb316e7c714
