diff --git a/skills/waves-claim/SKILL.md b/skills/waves-claim/SKILL.md new file mode 100644 index 0000000..3192729 --- /dev/null +++ b/skills/waves-claim/SKILL.md @@ -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", "jq"] +--- + +# 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 diff --git a/skills/waves-claim/install.sh b/skills/waves-claim/install.sh new file mode 100755 index 0000000..15b30c2 --- /dev/null +++ b/skills/waves-claim/install.sh @@ -0,0 +1,49 @@ +#!/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 Claim Skill..." + +# Create directories +mkdir -p "$SKILL_DIR/scripts" + +# Download files with error checking +download_file() { + local url="$1" + local dest="$2" + local http_code + + http_code=$(curl -sL -w "%{http_code}" -o "$dest" "$url") + + if [ "$http_code" != "200" ]; then + echo "❌ Failed to download $url (HTTP $http_code)" + rm -f "$dest" + return 1 + fi +} + +echo "📥 Downloading skill files..." + +download_file "$REPO_URL/SKILL.md" "$SKILL_DIR/SKILL.md" || exit 1 +download_file "$REPO_URL/scripts/check-claim.sh" "$SKILL_DIR/scripts/check-claim.sh" || exit 1 +download_file "$REPO_URL/scripts/claim.sh" "$SKILL_DIR/scripts/claim.sh" || exit 1 + +# Make scripts executable +chmod +x "$SKILL_DIR/scripts/"*.sh + +echo "" +echo "✅ Installed to: $SKILL_DIR" +echo "" +echo "📋 Prerequisites:" +echo " - Foundry cast: curl -L https://foundry.paradigm.xyz | bash" +echo " - jq: sudo apt install jq (or brew install jq)" +echo " - ETH on Base for gas (~0.0001 ETH)" +echo "" +echo "🚀 Usage:" +echo " Check status: $SKILL_DIR/scripts/check-claim.sh " +echo " Claim card: PRIVATE_KEY=0x... $SKILL_DIR/scripts/claim.sh" diff --git a/skills/waves-claim/scripts/check-claim.sh b/skills/waves-claim/scripts/check-claim.sh new file mode 100755 index 0000000..8f82a1a --- /dev/null +++ b/skills/waves-claim/scripts/check-claim.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# Check if wallet can claim from SURF Waves ClaimVault +# Usage: ./check-claim.sh [wallet_address] + +set -e + +# Contract addresses +CLAIM_VAULT="${CLAIM_VAULT:-0xAF1906B749339adaE38A1cba9740fffA168897c2}" +NFT_CONTRACT="${NFT_CONTRACT:-0xcc2d6ba8564541e6e51fe5522e26d4f4bbdd458b}" +RPC_URL="${RPC_URL:-https://mainnet.base.org}" + +# Check for cast +if ! command -v cast &> /dev/null; then + echo "❌ Foundry 'cast' not found. Install: curl -L https://foundry.paradigm.xyz | bash" + exit 1 +fi + +# Get wallet from arg or derive from private key +WALLET="${1:-}" +if [ -z "$WALLET" ] && [ -n "$PRIVATE_KEY" ]; then + WALLET=$(cast wallet address "$PRIVATE_KEY" 2>&1) || { + echo "❌ Failed to derive wallet from PRIVATE_KEY" + exit 1 + } +fi + +if [ -z "$WALLET" ]; then + echo "Usage: ./check-claim.sh " + echo " Or: PRIVATE_KEY=0x... ./check-claim.sh" + exit 1 +fi + +echo "🎴 SURF Waves ClaimVault Status" +echo " Vault: $CLAIM_VAULT" +echo " Wallet: $WALLET" +echo "" + +# Get available count (convert hex to decimal) +AVAILABLE_HEX=$(cast call "$CLAIM_VAULT" "availableCount()(uint256)" --rpc-url "$RPC_URL" 2>&1) || { + echo "❌ RPC error fetching availableCount: $AVAILABLE_HEX" + exit 1 +} +AVAILABLE=$(cast to-dec "$AVAILABLE_HEX" 2>/dev/null || echo "0") +echo "📦 Cards available: $AVAILABLE" + +# Check if can claim +CAN_CLAIM=$(cast call "$CLAIM_VAULT" "canClaim(address)(bool)" "$WALLET" --rpc-url "$RPC_URL" 2>&1) || { + echo "❌ RPC error checking canClaim: $CAN_CLAIM" + exit 1 +} + +if [ "$CAN_CLAIM" = "true" ]; then + echo "✅ You CAN claim now!" + echo "" + echo "Run: ./claim.sh" +else + # Get time until next claim (convert hex to decimal) + TIME_HEX=$(cast call "$CLAIM_VAULT" "timeUntilClaim(address)(uint256)" "$WALLET" --rpc-url "$RPC_URL" 2>&1) || { + echo "❌ RPC error fetching timeUntilClaim: $TIME_HEX" + exit 1 + } + TIME_LEFT=$(cast to-dec "$TIME_HEX" 2>/dev/null || echo "0") + MINUTES=$((TIME_LEFT / 60)) + SECONDS=$((TIME_LEFT % 60)) + echo "⏳ Cooldown: ${MINUTES}m ${SECONDS}s remaining" +fi diff --git a/skills/waves-claim/scripts/claim.sh b/skills/waves-claim/scripts/claim.sh new file mode 100755 index 0000000..9b00c40 --- /dev/null +++ b/skills/waves-claim/scripts/claim.sh @@ -0,0 +1,96 @@ +#!/bin/bash +# Claim a free SURF Waves Card NFT +# Usage: PRIVATE_KEY=0x... ./claim.sh + +set -e + +# Contract addresses +CLAIM_VAULT="${CLAIM_VAULT:-0xAF1906B749339adaE38A1cba9740fffA168897c2}" +RPC_URL="${RPC_URL:-https://mainnet.base.org}" + +# Check dependencies +if ! command -v cast &> /dev/null; then + echo "❌ Foundry 'cast' not found. Install: curl -L https://foundry.paradigm.xyz | bash" + exit 1 +fi + +if ! command -v jq &> /dev/null; then + echo "❌ 'jq' not found. Install: sudo apt install jq" + exit 1 +fi + +# Get private key +if [ -z "$PRIVATE_KEY" ]; then + echo "❌ PRIVATE_KEY environment variable required" + echo "Usage: PRIVATE_KEY=0x... ./claim.sh" + exit 1 +fi + +# Derive wallet address +WALLET=$(cast wallet address "$PRIVATE_KEY" 2>&1) || { + echo "❌ Invalid private key: $WALLET" + exit 1 +} + +echo "🎴 SURF Waves Card Claim" +echo " Wallet: $WALLET" +echo "" + +# Check if can claim (convert hex to decimal for comparison) +CAN_CLAIM=$(cast call "$CLAIM_VAULT" "canClaim(address)(bool)" "$WALLET" --rpc-url "$RPC_URL" 2>&1) || { + echo "❌ RPC error checking canClaim: $CAN_CLAIM" + exit 1 +} + +if [ "$CAN_CLAIM" != "true" ]; then + TIME_HEX=$(cast call "$CLAIM_VAULT" "timeUntilClaim(address)(uint256)" "$WALLET" --rpc-url "$RPC_URL" 2>&1) || { + echo "❌ RPC error: $TIME_HEX" + exit 1 + } + TIME_LEFT=$(cast to-dec "$TIME_HEX" 2>/dev/null || echo "0") + MINUTES=$((TIME_LEFT / 60)) + echo "⏳ Cooldown active. Try again in ${MINUTES} minutes." + exit 1 +fi + +# Check available cards +AVAILABLE_HEX=$(cast call "$CLAIM_VAULT" "availableCount()(uint256)" --rpc-url "$RPC_URL" 2>&1) || { + echo "❌ RPC error: $AVAILABLE_HEX" + exit 1 +} +AVAILABLE=$(cast to-dec "$AVAILABLE_HEX" 2>/dev/null || echo "0") + +if [ "$AVAILABLE" = "0" ]; then + echo "❌ No cards available in vault" + exit 1 +fi + +echo "📦 Cards available: $AVAILABLE" +echo "🚀 Claiming..." + +# Execute claim transaction +TX_OUTPUT=$(cast send "$CLAIM_VAULT" "claim()" \ + --private-key "$PRIVATE_KEY" \ + --rpc-url "$RPC_URL" \ + --json 2>&1) + +TX_STATUS=$? +if [ $TX_STATUS -ne 0 ]; then + echo "❌ Transaction failed: $TX_OUTPUT" + exit 1 +fi + +# Extract transaction hash +TX_HASH=$(echo "$TX_OUTPUT" | jq -r '.transactionHash // empty') + +if [ -z "$TX_HASH" ] || [ "$TX_HASH" = "null" ]; then + echo "❌ Failed to get transaction hash" + echo "Raw output: $TX_OUTPUT" + exit 1 +fi + +echo "" +echo "✅ Claim successful!" +echo "🔗 TX: https://basescan.org/tx/$TX_HASH" +echo "" +echo "View your cards: https://opensea.io/account"