-
-
Notifications
You must be signed in to change notification settings - Fork 320
feat: Add waves-claim skill for SURF Waves Cards #107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
| 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" | ||
vercel[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| # 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!" | ||
| 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 | ||
vercel[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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) | ||
|
|
||
|
||
| 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) | ||
|
||
| MINUTES=$((TIME_LEFT / 60)) | ||
| echo "⏳ Cooldown: ${MINUTES} minutes remaining" | ||
| fi | ||
| 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 | ||
|
|
||
vercel[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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 | ||
vercel[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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..." | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Convert Also appears in: Prompt To Fix With AIThis 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) | ||
|
|
||
vercel[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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:" | ||
|
||
| echo " https://opensea.io/collection/surf-waves-cards" | ||
| else | ||
| echo "❌ Claim failed" | ||
| echo "$TX" | ||
| exit 1 | ||
| fi | ||
Uh oh!
There was an error while loading. Please reload this page.