Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 195 additions & 0 deletions skills/waves-claim/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
---
name: waves-claim
description: Claim free SURF Waves Card NFTs on Base. One random card every 2 hours.
homepage: https://opensea.io/collection/surf-waves-cards
metadata:
openclaw:
emoji: "🎴"
category: "web3"
requires:
bins: ["cast"]
---

# Waves Claim Skill

Claim free SURF Waves Card NFTs on Base blockchain.

## Install

```bash
clawhub install waves-claim
```

Or one-click:
```bash
curl -sL https://raw.githubusercontent.com/openclaw/clawhub/main/skills/waves-claim/install.sh | bash
```

## Overview

The WavesTCG ClaimVault distributes free trading card NFTs. Any wallet can claim one random card every 2 hours.

## Contract Info

| Property | Value |
|----------|-------|
| **ClaimVault** | `0xAF1906B749339adaE38A1cba9740fffA168897c2` |
| **NFT Contract** | `0xcc2d6ba8564541e6e51fe5522e26d4f4bbdd458b` |
| **Network** | Base (Chain ID: 8453) |
| **Cooldown** | 2 hours |
| **RPC** | `https://mainnet.base.org` |

## Prerequisites

- Wallet with private key
- Small amount of ETH on Base for gas (~0.0001 ETH)
- Foundry's `cast` CLI (at `~/.foundry/bin/cast`)

## Quick Claim (cast)

```bash
export PATH="$HOME/.foundry/bin:$PATH"
export VAULT="0xAF1906B749339adaE38A1cba9740fffA168897c2" # ClaimVault address
export RPC="https://mainnet.base.org"
export PRIVATE_KEY="0x..." # Your private key

# 1. Check if you can claim
CAN_CLAIM=$(cast call $VAULT "canClaim(address)" $(cast wallet address --private-key $PRIVATE_KEY) --rpc-url $RPC)
echo "Can claim: $CAN_CLAIM"

# 2. If true, claim!
if [ "$CAN_CLAIM" = "0x0000000000000000000000000000000000000000000000000000000000000001" ]; then
cast send $VAULT "claim()" --rpc-url $RPC --private-key $PRIVATE_KEY
fi
```

## Check Cooldown

```bash
# Get seconds until next claim
cast call $VAULT "timeUntilClaim(address)" YOUR_ADDRESS --rpc-url $RPC | cast to-dec
```

## Check Available Cards

```bash
cast call $VAULT "availableCount()" --rpc-url $RPC | cast to-dec
```

## Full Claim Script

```bash
#!/bin/bash
# waves-claim.sh - Claim a free SURF Waves card

export PATH="$HOME/.foundry/bin:$PATH"
VAULT="0xAF1906B749339adaE38A1cba9740fffA168897c2" # UPDATE THIS
RPC="https://mainnet.base.org"

# Load private key from credentials
PRIVATE_KEY=$(jq -r '.private_key' ~/.config/clawtasks/credentials.json)
WALLET=$(cast wallet address --private-key $PRIVATE_KEY)

echo "🌊 SURF Waves Claim"
echo "Wallet: $WALLET"

# Check available
AVAILABLE=$(cast call $VAULT "availableCount()" --rpc-url $RPC | cast to-dec)
echo "Available cards: $AVAILABLE"

if [ "$AVAILABLE" = "0" ]; then
echo "❌ No cards available"
exit 1
fi

# Check cooldown
CAN_CLAIM=$(cast call $VAULT "canClaim(address)" $WALLET --rpc-url $RPC)

if [ "$CAN_CLAIM" != "0x0000000000000000000000000000000000000000000000000000000000000001" ]; then
REMAINING=$(cast call $VAULT "timeUntilClaim(address)" $WALLET --rpc-url $RPC | cast to-dec)
HOURS=$((REMAINING / 3600))
MINS=$(((REMAINING % 3600) / 60))
echo "⏰ Cooldown: ${HOURS}h ${MINS}m remaining"
exit 1
fi

echo "🎯 Claiming..."
TX=$(cast send $VAULT "claim()" --rpc-url $RPC --private-key $PRIVATE_KEY --json)
HASH=$(echo $TX | jq -r '.transactionHash')

echo "✅ Claimed! TX: $HASH"
echo "View on BaseScan: https://basescan.org/tx/$HASH"
```

## Scheduled Claiming

To claim automatically every 2 hours, use OpenClaw cron:

```
/cron add "waves-claim" "0 */2 * * *" "Run ~/.openclaw/skills/waves-claim/claim.sh and report result"
```

## JavaScript Example

```javascript
import { createPublicClient, createWalletClient, http } from 'viem';
import { base } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';

const VAULT = '0x...'; // ClaimVault address

const abi = [
'function canClaim(address) view returns (bool)',
'function availableCount() view returns (uint256)',
'function claim()'
];

export async function claimWavesCard(privateKey) {
const account = privateKeyToAccount(privateKey);

const publicClient = createPublicClient({
chain: base,
transport: http()
});

const walletClient = createWalletClient({
account,
chain: base,
transport: http()
});

// Check eligibility
const [canClaim, available] = await Promise.all([
publicClient.readContract({ address: VAULT, abi, functionName: 'canClaim', args: [account.address] }),
publicClient.readContract({ address: VAULT, abi, functionName: 'availableCount' })
]);

if (!canClaim) return { success: false, reason: 'cooldown' };
if (available === 0n) return { success: false, reason: 'empty' };

const hash = await walletClient.writeContract({
address: VAULT,
abi,
functionName: 'claim'
});

return { success: true, txHash: hash };
}
```

## Troubleshooting

**"Cooldown not expired"**
Wait for 2 hours since your last claim.

**"No NFTs available"**
The vault is empty. Cards are periodically added by the owner.

**"insufficient funds"**
You need ETH on Base for gas. Bridge from mainnet or get from faucet.

## Links

- **Mini-App**: https://wavestcg.xyz/claim
- **OpenSea**: https://opensea.io/collection/surf-waves-cards
- **BaseScan NFT**: https://basescan.org/token/0xcc2d6ba8564541e6e51fe5522e26d4f4bbdd458b
36 changes: 36 additions & 0 deletions skills/waves-claim/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/bash
# One-click install for SURF Waves Cards auto-claim skill
# Usage: curl -sL https://raw.githubusercontent.com/openclaw/clawhub/main/skills/waves-claim/install.sh | bash

set -e

SKILL_DIR="${OPENCLAW_SKILLS:-$HOME/.openclaw/skills}/waves-claim"
REPO_URL="https://raw.githubusercontent.com/openclaw/clawhub/main/skills/waves-claim"

echo "🎴 Installing SURF Waves Cards claim skill..."

# Create skill directory
mkdir -p "$SKILL_DIR/scripts"

# Download skill files
echo "📥 Downloading skill files..."
curl -sL "$REPO_URL/SKILL.md" > "$SKILL_DIR/SKILL.md"
curl -sL "$REPO_URL/scripts/check-claim.sh" > "$SKILL_DIR/scripts/check-claim.sh"
curl -sL "$REPO_URL/scripts/claim.sh" > "$SKILL_DIR/scripts/claim.sh"

# Make scripts executable
chmod +x "$SKILL_DIR/scripts/"*.sh

echo ""
echo "✅ Skill installed to: $SKILL_DIR"
echo ""
echo "📋 Usage:"
echo " Check eligibility: $SKILL_DIR/scripts/check-claim.sh"
echo " Claim a card: $SKILL_DIR/scripts/claim.sh"
echo ""
echo "🔧 Requirements:"
echo " - Foundry (cast CLI)"
echo " - PRIVATE_KEY env var or ~/.config/clawtasks/credentials.json"
echo " - Base ETH for gas"
echo ""
echo "🌊 Happy claiming!"
46 changes: 46 additions & 0 deletions skills/waves-claim/scripts/check-claim.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/bin/bash
# Check if wallet can claim a SURF Waves Card

CLAIM_VAULT="${CLAIM_VAULT:-0xAF1906B749339adaE38A1cba9740fffA168897c2}"
RPC_URL="${RPC_URL:-https://mainnet.base.org}"

# Get wallet address from private key or env
if [ -n "$PRIVATE_KEY" ]; then
WALLET=$(cast wallet address --private-key "$PRIVATE_KEY" 2>/dev/null)
elif [ -n "$WALLET" ]; then
WALLET="$WALLET"
else
# Try clawtasks credentials
if [ -f ~/.config/clawtasks/credentials.json ]; then
PRIVATE_KEY=$(jq -r '.private_key' ~/.config/clawtasks/credentials.json)
WALLET=$(cast wallet address --private-key "$PRIVATE_KEY" 2>/dev/null)
fi
fi

if [ -z "$WALLET" ]; then
echo "❌ Set PRIVATE_KEY or WALLET env variable"
exit 1
fi

echo "🎴 SURF Waves Cards - Claim Check"
echo " Wallet: $WALLET"
echo " Vault: $CLAIM_VAULT"
echo ""

# Check available count
AVAILABLE=$(cast call "$CLAIM_VAULT" "availableCount()(uint256)" --rpc-url "$RPC_URL" 2>/dev/null)
echo "📦 Available NFTs: $AVAILABLE"

# Check if can claim
CAN_CLAIM=$(cast call "$CLAIM_VAULT" "canClaim(address)(bool)" "$WALLET" --rpc-url "$RPC_URL" 2>/dev/null)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cast call ... availableCount()(uint256) returns a 32-byte hex value by default (e.g. 0x...), but the script compares it as a decimal string later ([ "$AVAILABLE" = "0" ] in claim.sh) and prints it as if it’s a human-readable count. This will show confusing output and can mislead users into thinking there are NFTs available when there aren’t (or vice versa).

Consider piping through cast to-dec (and similarly for timeUntilClaim) so the values are consistent and numeric.

Also appears in: skills/waves-claim/scripts/claim.sh:35-41

Prompt To Fix With AI
This is a comment left during a code review.
Path: skills/waves-claim/scripts/check-claim.sh
Line: 35:36

Comment:
`cast call ... availableCount()(uint256)` returns a 32-byte hex value by default (e.g. `0x...`), but the script compares it as a decimal string later (`[ "$AVAILABLE" = "0" ]` in `claim.sh`) and prints it as if it’s a human-readable count. This will show confusing output and can mislead users into thinking there are NFTs available when there aren’t (or vice versa).

Consider piping through `cast to-dec` (and similarly for `timeUntilClaim`) so the values are consistent and numeric.

Also appears in: `skills/waves-claim/scripts/claim.sh:35-41`

How can I resolve this? If you propose a fix, please make it concise.

if [ "$CAN_CLAIM" = "true" ]; then
echo "✅ You can claim now!"
echo ""
echo "Run: ./scripts/claim.sh"
else
# Get time remaining
TIME_LEFT=$(cast call "$CLAIM_VAULT" "timeUntilClaim(address)(uint256)" "$WALLET" --rpc-url "$RPC_URL" 2>/dev/null)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIME_LEFT is a hex string from cast call (e.g. 0x...), but it’s used in arithmetic expansion ($((TIME_LEFT / 60))). Bash arithmetic doesn’t accept 0x... unless it’s in 0xNN form without quotes/extra characters; in practice this will error or produce 0 depending on shell.

Pipe through cast to-dec (or printf "%d") before doing math.

Also appears in: skills/waves-claim/scripts/claim.sh:29-31

Prompt To Fix With AI
This is a comment left during a code review.
Path: skills/waves-claim/scripts/check-claim.sh
Line: 42:43

Comment:
`TIME_LEFT` is a hex string from `cast call` (e.g. `0x...`), but it’s used in arithmetic expansion (`$((TIME_LEFT / 60))`). Bash arithmetic doesn’t accept `0x...` unless it’s in `0xNN` form without quotes/extra characters; in practice this will error or produce 0 depending on shell.

Pipe through `cast to-dec` (or `printf "%d"`) before doing math.

Also appears in: `skills/waves-claim/scripts/claim.sh:29-31`

How can I resolve this? If you propose a fix, please make it concise.

MINUTES=$((TIME_LEFT / 60))
echo "⏳ Cooldown: ${MINUTES} minutes remaining"
fi
64 changes: 64 additions & 0 deletions skills/waves-claim/scripts/claim.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/bin/bash
# Claim a free SURF Waves Card NFT

CLAIM_VAULT="${CLAIM_VAULT:-0xAF1906B749339adaE38A1cba9740fffA168897c2}"
RPC_URL="${RPC_URL:-https://mainnet.base.org}"

# Get private key
if [ -z "$PRIVATE_KEY" ]; then
if [ -f ~/.config/clawtasks/credentials.json ]; then
PRIVATE_KEY=$(jq -r '.private_key' ~/.config/clawtasks/credentials.json)
fi
fi

if [ -z "$PRIVATE_KEY" ]; then
echo "❌ Set PRIVATE_KEY env variable"
exit 1
fi

WALLET=$(cast wallet address --private-key "$PRIVATE_KEY" 2>/dev/null)

echo "🎴 SURF Waves Cards - Claiming..."
echo " Wallet: $WALLET"
echo " Vault: $CLAIM_VAULT"
echo ""

# Check if can claim first
CAN_CLAIM=$(cast call "$CLAIM_VAULT" "canClaim(address)(bool)" "$WALLET" --rpc-url "$RPC_URL" 2>/dev/null)

if [ "$CAN_CLAIM" != "true" ]; then
TIME_LEFT=$(cast call "$CLAIM_VAULT" "timeUntilClaim(address)(uint256)" "$WALLET" --rpc-url "$RPC_URL" 2>/dev/null)
MINUTES=$((TIME_LEFT / 60))
echo "⏳ Cannot claim yet. Cooldown: ${MINUTES} minutes remaining"
exit 1
fi

# Check available
AVAILABLE=$(cast call "$CLAIM_VAULT" "availableCount()(uint256)" --rpc-url "$RPC_URL" 2>/dev/null)
if [ "$AVAILABLE" = "0" ]; then
echo "❌ No NFTs available in vault"
exit 1
fi

echo "📦 Available: $AVAILABLE NFTs"
echo "🔨 Sending claim transaction..."

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AVAILABLE is the raw cast call output (32-byte hex), so [ "$AVAILABLE" = "0" ] will never be true when the vault is empty (it’ll be 0x00..00). This causes the script to proceed to claim() even when no NFTs are available.

Convert AVAILABLE to decimal (or explicitly compare to the full zero hex) before checking.

Also appears in: skills/waves-claim/scripts/check-claim.sh:35-36

Prompt To Fix With AI
This is a comment left during a code review.
Path: skills/waves-claim/scripts/claim.sh
Line: 45:45

Comment:
`AVAILABLE` is the raw `cast call` output (32-byte hex), so `[ "$AVAILABLE" = "0" ]` will never be true when the vault is empty (it’ll be `0x00..00`). This causes the script to proceed to `claim()` even when no NFTs are available.

Convert `AVAILABLE` to decimal (or explicitly compare to the full zero hex) before checking.

Also appears in: `skills/waves-claim/scripts/check-claim.sh:35-36`

How can I resolve this? If you propose a fix, please make it concise.

# Claim
TX=$(cast send "$CLAIM_VAULT" "claim()" \
--private-key "$PRIVATE_KEY" \
--rpc-url "$RPC_URL" \
--json 2>/dev/null)

if [ $? -eq 0 ]; then
TX_HASH=$(echo "$TX" | jq -r '.transactionHash')
echo ""
echo "✅ Claimed successfully!"
echo " TX: https://basescan.org/tx/$TX_HASH"
echo ""
echo "🎴 Check your new card on OpenSea:"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TX=$(cast send ... --json 2>/dev/null) is a command substitution, so if cast fails, the shell will still set TX (likely empty) and $? will be the status of the assignment, not reliably the cast exit code. This makes the success/failure branch unreliable.

Run cast send directly in the if condition (or capture the exit code immediately) so failures are handled correctly.

Prompt To Fix With AI
This is a comment left during a code review.
Path: skills/waves-claim/scripts/claim.sh
Line: 56:58

Comment:
`TX=$(cast send ... --json 2>/dev/null)` is a command substitution, so if `cast` fails, the shell will still set `TX` (likely empty) and `$?` will be the status of the assignment, not reliably the `cast` exit code. This makes the success/failure branch unreliable.

Run `cast send` directly in the `if` condition (or capture the exit code immediately) so failures are handled correctly.

How can I resolve this? If you propose a fix, please make it concise.

echo " https://opensea.io/collection/surf-waves-cards"
else
echo "❌ Claim failed"
echo "$TX"
exit 1
fi