Back to skills
SkillHub ClubShip Full StackFull Stack

gotchi-dao-voting

Check active Aavegotchi DAO proposals and vote on Snapshot via Bankr EIP-712 signatures.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
3,122
Hot score
99
Updated
March 20, 2026
Overall rating
C4.0
Composite score
4.0
Best-practice grade
C65.6

Install command

npx @skill-hub/cli install openclaw-skills-gotchi-dao-voting

Repository

openclaw/skills

Skill path: skills/aaigotchi/gotchi-dao-voting

Check active Aavegotchi DAO proposals and vote on Snapshot via Bankr EIP-712 signatures.

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: openclaw.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install gotchi-dao-voting into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/openclaw/skills before adding gotchi-dao-voting to shared team environments
  • Use gotchi-dao-voting for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: gotchi-dao-voting
description: Check active Aavegotchi DAO proposals and vote on Snapshot via Bankr EIP-712 signatures.
homepage: https://github.com/aaigotchi/gotchi-dao-voting
metadata:
  openclaw:
    requires:
      bins:
        - curl
        - jq
      env:
        - BANKR_API_KEY
---

# gotchi-dao-voting

Vote on Snapshot proposals for `aavegotchi.eth`.

## Scripts

- `./scripts/list-proposals.sh`
  - Lists active proposals and your VP per proposal.
- `./scripts/vote.sh [--dry-run] <proposal-id> <choice>`
  - Submits signed vote through Snapshot sequencer.
  - `--dry-run` prints typed data and exits without signing/submitting.

## Choice Formats

- Single-choice proposal: numeric option, e.g. `2`
- Weighted proposal: JSON object string, e.g. `'{"2":2238}'`
  - If you pass just `2` for a weighted vote, script auto-converts to `{"2":<floor(vp)>}`.

## Config

`config.json` keys:
- `wallet`
- `space`
- `snapshotApiUrl`
- `snapshotSequencer`

## Security

- Uses Bankr signing API (no local private key usage).
- Off-chain Snapshot voting (no gas transaction).
- Input validation for proposal ID, wallet, choice format, and choice range.


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### scripts/list-proposals.sh

```bash
#!/usr/bin/env bash
# List active Aavegotchi DAO proposals on Snapshot

set -euo pipefail

usage() {
  cat <<USAGE
Usage: ./scripts/list-proposals.sh

Lists active proposals in configured Snapshot space and prints
wallet voting power per proposal.
USAGE
}

if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
  usage
  exit 0
fi

if [ "$#" -gt 0 ]; then
  usage
  exit 1
fi

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib.sh
source "$SCRIPT_DIR/lib.sh"

require_tools
load_config

echo "πŸ—³οΈ  AAVEGOTCHI DAO ACTIVE PROPOSALS"
echo "==================================="
echo

QUERY='query($space:String!){ proposals(first: 20, skip: 0, where: {space_in: [$space], state: "active"}, orderBy: "created", orderDirection: desc) { id title end state choices type } }'
VARS="$(jq -n --arg space "$SPACE" '{space:$space}')"
PROPOSALS="$(snapshot_query "$QUERY" "$VARS")"

if snapshot_has_errors "$PROPOSALS"; then
  echo "❌ Snapshot query error"
  echo "$PROPOSALS" | jq '.errors'
  exit 1
fi

COUNT="$(echo "$PROPOSALS" | jq '.data.proposals | length')"
if [ "$COUNT" = "0" ]; then
  echo "πŸ“­ No active proposals found"
  echo
  echo "πŸ”— Check: https://snapshot.org/#/$SPACE"
  exit 0
fi

echo "πŸ“Š Found $COUNT active proposal(s)"
echo

echo "$PROPOSALS" | jq -r '.data.proposals[] | @base64' | while read -r p64; do
  proposal="$(printf '%s' "$p64" | base64 -d)"
  ID="$(echo "$proposal" | jq -r '.id')"
  TITLE="$(echo "$proposal" | jq -r '.title')"
  TYPE="$(echo "$proposal" | jq -r '.type')"
  END="$(echo "$proposal" | jq -r '.end')"
  CHOICES="$(echo "$proposal" | jq -r '.choices | length')"

  END_DATE="$(format_utc "$END")"

  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "πŸ“‹ $TITLE"
  echo
  echo "   ID: $ID"
  echo "   Type: $TYPE"
  echo "   Choices: $CHOICES"
  echo "   Ends: $END_DATE"
  echo

  VP_QUERY='query($voter:String!, $space:String!, $proposal:String!){ vp(voter: $voter, space: $space, proposal: $proposal) { vp } }'
  VP_VARS="$(jq -n --arg voter "$WALLET" --arg space "$SPACE" --arg proposal "$ID" '{voter:$voter,space:$space,proposal:$proposal}')"
  VP_DATA="$(snapshot_query "$VP_QUERY" "$VP_VARS")"

  if snapshot_has_errors "$VP_DATA"; then
    echo "   ⚠️  Could not fetch VP"
  else
    VP="$(echo "$VP_DATA" | jq -r '.data.vp.vp // 0')"
    if [ "$VP" != "0" ] && [ "$VP" != "null" ]; then
      printf "   πŸ’ͺ Your VP: %.2f\n" "$VP"
    else
      echo "   ⚠️  Your VP: 0 (cannot vote)"
    fi
  fi

  echo "   πŸ”— https://snapshot.org/#/$SPACE/proposal/$ID"
  echo
done

echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo
echo "πŸ’‘ To vote, use: ./scripts/vote.sh <proposal-id> <choice>"

```

### scripts/vote.sh

```bash
#!/usr/bin/env bash
# Vote on Aavegotchi Snapshot proposals via Bankr signature

set -euo pipefail

usage() {
  cat <<USAGE
Usage: $0 [--dry-run] <proposal-id> <choice>

Examples:
  Single-choice: $0 0xabc123... 2
  Weighted:      $0 0xabc123... '{"2": 2238}'
  Dry run:       $0 --dry-run 0xabc123... 2
USAGE
}

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib.sh
source "$SCRIPT_DIR/lib.sh"

DRY_RUN=0
POSITIONAL=()
while [ "$#" -gt 0 ]; do
  case "$1" in
    --dry-run)
      DRY_RUN=1
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      POSITIONAL+=("$1")
      shift
      ;;
  esac
done

if [ "${#POSITIONAL[@]}" -lt 2 ]; then
  usage
  exit 1
fi

PROPOSAL_ID="$(normalize_proposal_id "${POSITIONAL[0]}")"
CHOICE_RAW="${POSITIONAL[1]}"

require_tools
load_config
API_KEY="$(resolve_bankr_api_key)"

TMP_TYPED="$(mktemp /tmp/vote-typed-XXXXXX.json)"
TMP_PAYLOAD="$(mktemp /tmp/vote-payload-XXXXXX.json)"
cleanup() {
  rm -f "$TMP_TYPED" "$TMP_PAYLOAD"
}
trap cleanup EXIT

echo "πŸ—³οΈ  AAVEGOTCHI DAO VOTING"
echo "========================"
echo
printf "πŸ‘€ Wallet: %s\n" "$WALLET"
printf "πŸ“‹ Proposal: %s\n" "$PROPOSAL_ID"
printf "βœ… Choice: %s\n" "$CHOICE_RAW"
echo

echo "πŸ” Fetching proposal details..."
P_QUERY='query($id:String!){ proposal(id: $id) { id title type choices state } }'
P_VARS="$(jq -n --arg id "$PROPOSAL_ID" '{id:$id}')"
PROPOSAL_DATA="$(snapshot_query "$P_QUERY" "$P_VARS")"

if snapshot_has_errors "$PROPOSAL_DATA"; then
  echo "❌ Snapshot proposal query error"
  echo "$PROPOSAL_DATA" | jq '.errors'
  exit 1
fi

if [ "$(echo "$PROPOSAL_DATA" | jq -r '.data.proposal.id // empty')" = "" ]; then
  echo "❌ Proposal not found: $PROPOSAL_ID"
  exit 1
fi

TITLE="$(echo "$PROPOSAL_DATA" | jq -r '.data.proposal.title')"
TYPE="$(echo "$PROPOSAL_DATA" | jq -r '.data.proposal.type')"
STATE="$(echo "$PROPOSAL_DATA" | jq -r '.data.proposal.state')"
CHOICE_COUNT="$(echo "$PROPOSAL_DATA" | jq -r '.data.proposal.choices | length')"

echo "πŸ“ Title: $TITLE"
echo "🎯 Type: $TYPE"
echo "⚑ State: $STATE"
echo

echo "πŸ“Š Available choices:"
echo "$PROPOSAL_DATA" | jq -r '.data.proposal.choices[]' | nl

if [ "$STATE" != "active" ]; then
  echo
  echo "⚠️  Proposal is not active (state: $STATE); sequencer may reject this vote."
fi

echo
echo "πŸ’ͺ Checking voting power..."
VP_QUERY='query($voter:String!, $space:String!, $proposal:String!){ vp(voter: $voter, space: $space, proposal: $proposal) { vp vp_by_strategy } }'
VP_VARS="$(jq -n --arg voter "$WALLET" --arg space "$SPACE" --arg proposal "$PROPOSAL_ID" '{voter:$voter,space:$space,proposal:$proposal}')"
VP_DATA="$(snapshot_query "$VP_QUERY" "$VP_VARS")"

if snapshot_has_errors "$VP_DATA"; then
  echo "❌ Snapshot VP query error"
  echo "$VP_DATA" | jq '.errors'
  exit 1
fi

VP="$(echo "$VP_DATA" | jq -r '.data.vp.vp // 0')"
VP_BY_STRATEGY="$(echo "$VP_DATA" | jq -c '.data.vp.vp_by_strategy // []')"
echo "   Total VP: $VP"
echo "   Breakdown: $VP_BY_STRATEGY"

if [ "$VP" = "0" ] || [ "$VP" = "null" ]; then
  echo "❌ You have 0 voting power on this proposal"
  exit 1
fi

CHOICE_TYPE="uint32"
CHOICE_VALUE="$CHOICE_RAW"

if [ "$TYPE" = "weighted" ]; then
  CHOICE_TYPE="string"

  if [[ "$CHOICE_RAW" =~ ^\{.*\}$ ]]; then
    echo "$CHOICE_RAW" | jq -e --argjson max "$CHOICE_COUNT" '
      type == "object" and
      (keys | length > 0) and
      all(keys[]; test("^[0-9]+$") and ((tonumber >= 1) and (tonumber <= $max))) and
      all(.[]; (type == "number") and (. >= 0))
    ' >/dev/null 2>&1 || err "Invalid weighted JSON choice (keys must be 1..$CHOICE_COUNT, values numeric >= 0)"
    CHOICE_VALUE="$(echo "$CHOICE_RAW" | jq -c '.')"
  elif [[ "$CHOICE_RAW" =~ ^[0-9]+$ ]]; then
    [ "$CHOICE_RAW" -ge 1 ] && [ "$CHOICE_RAW" -le "$CHOICE_COUNT" ] || err "Choice out of range (1..$CHOICE_COUNT)"
    VP_FLOOR="$(printf '%.0f' "$VP")"
    CHOICE_VALUE="$(jq -n --arg key "$CHOICE_RAW" --argjson vp "$VP_FLOOR" '{($key):$vp}' | jq -c '.')"
    echo "πŸ’‘ Converted weighted choice: $CHOICE_VALUE"
  else
    err "Weighted voting requires numeric choice or JSON object"
  fi
else
  [[ "$CHOICE_RAW" =~ ^[0-9]+$ ]] || err "Single-choice voting requires numeric choice"
  [ "$CHOICE_RAW" -ge 1 ] && [ "$CHOICE_RAW" -le "$CHOICE_COUNT" ] || err "Choice out of range (1..$CHOICE_COUNT)"
  CHOICE_VALUE="$CHOICE_RAW"
fi

TIMESTAMP="$(date +%s)"

if [ "$CHOICE_TYPE" = "string" ]; then
  jq -n \
    --arg from "$WALLET" \
    --arg space "$SPACE" \
    --arg proposal "$PROPOSAL_ID" \
    --arg choice "$CHOICE_VALUE" \
    --arg app "openclaw" \
    --arg metadata "{}" \
    --argjson timestamp "$TIMESTAMP" \
    '{
      types: {
        Vote: [
          {name:"from",type:"address"},
          {name:"space",type:"string"},
          {name:"timestamp",type:"uint64"},
          {name:"proposal",type:"bytes32"},
          {name:"choice",type:"string"},
          {name:"reason",type:"string"},
          {name:"app",type:"string"},
          {name:"metadata",type:"string"}
        ]
      },
      domain: {name:"snapshot",version:"0.1.4"},
      primaryType:"Vote",
      message: {from:$from,space:$space,timestamp:$timestamp,proposal:$proposal,choice:$choice,reason:"",app:$app,metadata:$metadata}
    }' > "$TMP_TYPED"
else
  jq -n \
    --arg from "$WALLET" \
    --arg space "$SPACE" \
    --arg proposal "$PROPOSAL_ID" \
    --arg app "openclaw" \
    --arg metadata "{}" \
    --argjson timestamp "$TIMESTAMP" \
    --argjson choice "$CHOICE_VALUE" \
    '{
      types: {
        Vote: [
          {name:"from",type:"address"},
          {name:"space",type:"string"},
          {name:"timestamp",type:"uint64"},
          {name:"proposal",type:"bytes32"},
          {name:"choice",type:"uint32"},
          {name:"reason",type:"string"},
          {name:"app",type:"string"},
          {name:"metadata",type:"string"}
        ]
      },
      domain: {name:"snapshot",version:"0.1.4"},
      primaryType:"Vote",
      message: {from:$from,space:$space,timestamp:$timestamp,proposal:$proposal,choice:$choice,reason:"",app:$app,metadata:$metadata}
    }' > "$TMP_TYPED"
fi

echo
echo "πŸ“ Typed data prepared"

if [ "$DRY_RUN" -eq 1 ]; then
  echo "--- DRY RUN ---"
  cat "$TMP_TYPED" | jq '.'
  exit 0
fi

echo "πŸ“ Signing vote with Bankr..."
SIGN_PAYLOAD="$(jq -n --slurpfile typed "$TMP_TYPED" '{signatureType:"eth_signTypedData_v4",typedData:$typed[0]}')"

SIGN_RESPONSE="$(curl -sS -X POST "https://api.bankr.bot/agent/sign" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d "$SIGN_PAYLOAD")"

SIGNATURE="$(echo "$SIGN_RESPONSE" | jq -r '.signature // empty')"

if [ -z "$SIGNATURE" ]; then
  echo "❌ Failed to get signature from Bankr"
  echo "$SIGN_RESPONSE" | jq '.'
  exit 1
fi

echo "βœ… Signature obtained"

echo "πŸ“€ Submitting vote to Snapshot..."

jq -n \
  --arg address "$WALLET" \
  --arg sig "$SIGNATURE" \
  --slurpfile data "$TMP_TYPED" \
  '{address:$address,sig:$sig,data:$data[0]}' > "$TMP_PAYLOAD"

VOTE_RESPONSE="$(curl -sS -X POST "$SEQUENCER" -H "Content-Type: application/json" -d @"$TMP_PAYLOAD")"

echo "πŸ“¬ Response:"
echo "$VOTE_RESPONSE" | jq '.'

if echo "$VOTE_RESPONSE" | jq -e '.id' >/dev/null 2>&1; then
  VOTE_ID="$(echo "$VOTE_RESPONSE" | jq -r '.id')"
  IPFS="$(echo "$VOTE_RESPONSE" | jq -r '.ipfs // empty')"
  echo
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "βœ… VOTE SUCCESSFUL"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "πŸ“‹ Vote ID: $VOTE_ID"
  if [ -n "$IPFS" ]; then
    echo "πŸ“¦ IPFS: $IPFS"
  fi
  echo "πŸ”— View: https://snapshot.org/#/$SPACE/proposal/$PROPOSAL_ID"
else
  ERROR="$(echo "$VOTE_RESPONSE" | jq -r '.error_description // .error // "Unknown error"')"
  echo "❌ Vote failed: $ERROR"
  exit 1
fi

```



---

## Skill Companion Files

> Additional files collected from the skill directory layout.

### README.md

```markdown
# Gotchi DAO Voting

Snapshot voting automation for Aavegotchi DAO (`aavegotchi.eth`) using Bankr signing.

## Scripts

- `./scripts/list-proposals.sh`
  - Lists active proposals + your current VP for each
- `./scripts/vote.sh [--dry-run] <proposal-id> <choice>`
  - Single choice: `2`
  - Weighted choice: `'{"2":2238}'`

## Quick Start

```bash
# 1) List active proposals
./scripts/list-proposals.sh

# 2) Preview typed vote payload (safe)
./scripts/vote.sh --dry-run <proposal-id> 2

# 3) Submit vote
./scripts/vote.sh <proposal-id> 2
```

## Requirements

- `curl`, `jq`
- `BANKR_API_KEY` (env recommended)

Bankr API key resolution order:
1. `BANKR_API_KEY`
2. user systemd environment (`systemctl --user show-environment`)
3. `~/.openclaw/skills/bankr/config.json`
4. `~/.openclaw/workspace/skills/bankr/config.json`

## Config

`config.json`:

```json
{
  "wallet": "0xYourBankrWallet",
  "space": "aavegotchi.eth",
  "snapshotApiUrl": "https://hub.snapshot.org/graphql",
  "snapshotSequencer": "https://seq.snapshot.org/"
}
```

## Notes

- Snapshot voting is off-chain (no gas fee).
- Voting still requires correct VP at proposal snapshot block.
- `--dry-run` builds typed data without signing/submitting.

```

### _meta.json

```json
{
  "owner": "aaigotchi",
  "slug": "gotchi-dao-voting",
  "displayName": "Gotchi DAO Voting",
  "latest": {
    "version": "1.1.0",
    "publishedAt": 1772826869163,
    "commit": "https://github.com/openclaw/skills/commit/6b803680e0e3c0128bbaf60a4b73c0eee10e8ad4"
  },
  "history": [
    {
      "version": "1.0.0",
      "publishedAt": 1771690950048,
      "commit": "https://github.com/openclaw/skills/commit/eedf7a87e5cda72421b243f9c7cdbf07e55a71f8"
    }
  ]
}

```

### scripts/lib.sh

```bash
#!/usr/bin/env bash

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="${GOTCHI_DAO_CONFIG_FILE:-$SCRIPT_DIR/../config.json}"

err() {
  echo "ERROR: $*" >&2
  exit 1
}

require_bin() {
  local bin="$1"
  command -v "$bin" >/dev/null 2>&1 || err "Missing required binary: $bin"
}

require_tools() {
  require_bin curl
  require_bin jq
}

normalize_wallet() {
  local wallet="$1"
  [[ "$wallet" =~ ^0x[0-9a-fA-F]{40}$ ]] || err "Invalid wallet address: $wallet"
  printf '%s\n' "$wallet"
}

normalize_proposal_id() {
  local proposal_id="$1"
  [[ "$proposal_id" =~ ^0x[0-9a-fA-F]{64}$ ]] || err "Invalid proposal ID: $proposal_id"
  printf '%s\n' "$proposal_id"
}

load_config() {
  [ -f "$CONFIG_FILE" ] || err "Config file not found: $CONFIG_FILE"

  WALLET="$(jq -r '.wallet // empty' "$CONFIG_FILE")"
  SPACE="$(jq -r '.space // empty' "$CONFIG_FILE")"
  SNAPSHOT_API="$(jq -r '.snapshotApiUrl // empty' "$CONFIG_FILE")"
  SEQUENCER="$(jq -r '.snapshotSequencer // empty' "$CONFIG_FILE")"

  [ -n "$WALLET" ] || err "Missing config.wallet"
  [ -n "$SPACE" ] || err "Missing config.space"
  [ -n "$SNAPSHOT_API" ] || err "Missing config.snapshotApiUrl"
  [ -n "$SEQUENCER" ] || err "Missing config.snapshotSequencer"

  normalize_wallet "$WALLET" >/dev/null
}

resolve_bankr_api_key() {
  local key="${BANKR_API_KEY:-}"

  if [ -z "$key" ] && command -v systemctl >/dev/null 2>&1; then
    key="$(systemctl --user show-environment 2>/dev/null | sed -n 's/^BANKR_API_KEY=//p' | head -n1 || true)"
  fi

  if [ -z "$key" ] && [ -f "$HOME/.openclaw/skills/bankr/config.json" ]; then
    key="$(jq -r '.apiKey // empty' "$HOME/.openclaw/skills/bankr/config.json" 2>/dev/null || true)"
  fi

  if [ -z "$key" ] && [ -f "$HOME/.openclaw/workspace/skills/bankr/config.json" ]; then
    key="$(jq -r '.apiKey // empty' "$HOME/.openclaw/workspace/skills/bankr/config.json" 2>/dev/null || true)"
  fi

  [ -n "$key" ] || err "BANKR_API_KEY missing (env/systemd/bankr config)"
  printf '%s\n' "$key"
}

snapshot_query() {
  local query="$1"
  local variables_json="${2-}"
  local payload

  if [ -z "$variables_json" ]; then
    variables_json='{}'
  fi

  printf '%s' "$variables_json" | jq -e . >/dev/null 2>&1 || err "Invalid Snapshot variables JSON"

  payload="$(jq -n --arg query "$query" --argjson variables "$variables_json" '{query:$query,variables:$variables}')" || err "Failed to build Snapshot query payload"

  curl -sS -X POST "$SNAPSHOT_API" \
    -H "Content-Type: application/json" \
    -d "$payload"
}

snapshot_has_errors() {
  local response="$1"
  printf '%s' "$response" | jq -e '.errors and (.errors | length > 0)' >/dev/null 2>&1
}

format_utc() {
  local ts="$1"
  date -u -d "@$ts" '+%Y-%m-%d %H:%M UTC' 2>/dev/null || echo "Unknown"
}

```