vladthecto-crewmind-bets
安装 / 下载方式
TotalClaw CLI推荐
totalclaw install clawskills:clawskills~vladthecto-crewmind-betscURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/clawskills%3Aclawskills~vladthecto-crewmind-bets/file -o vladthecto-crewmind-bets.md# 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.f