docs: apply fleet-template (16-artifact scaffold)
Adds missing standard artifacts: - README.md (if missing) - AGENTS.md (AI agent contract) - PLAN.md (current sprint) - STATUS.md (where we are) - DEVELOPMENT.md (dev workflow) - DEPLOYMENT.md (deploy procedure) - TESTING.md (test strategy) - DECISIONS.md (ADR index + templates) - .github/CODEOWNERS - .github/workflows/ci.yml Preserves all existing artifacts. Refs: RugMunchMedia/fleet-template
This commit is contained in:
commit
e13bd4d774
203 changed files with 31140 additions and 0 deletions
650
cli/walletpress
Executable file
650
cli/walletpress
Executable file
|
|
@ -0,0 +1,650 @@
|
|||
#!/usr/bin/env bash
|
||||
# WalletPress CLI — Self-Hosted Wallet Management Platform
|
||||
# Usage: walletpress <command> [options]
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="1.0.0-beta"
|
||||
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/walletpress"
|
||||
CONFIG_FILE="$CONFIG_DIR/config.json"
|
||||
DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/walletpress"
|
||||
GATES_FILE="$DATA_DIR/gates.json"
|
||||
LOG_FILE="$DATA_DIR/walletpress.log"
|
||||
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
|
||||
|
||||
mkdir -p "$CONFIG_DIR" "$DATA_DIR"
|
||||
touch "$GATES_FILE" "$LOG_FILE"
|
||||
|
||||
log() { echo "[$(date +%H:%M:%S)] $*" >> "$LOG_FILE"; }
|
||||
info() { echo -e "${BLUE}::${NC} $*"; }
|
||||
ok() { echo -e "${GREEN}✓${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}⚠${NC} $*"; }
|
||||
err() { echo -e "${RED}✗${NC} $*" >&2; }
|
||||
hr() { printf '%*s\n' "${COLUMNS:-80}" '' | tr ' ' '─'; }
|
||||
die() { err "$1"; exit 1; }
|
||||
|
||||
# ─── Config ──────────────────────────────────────────────────────────
|
||||
|
||||
load_config() {
|
||||
if [[ -f "$CONFIG_FILE" ]]; then
|
||||
API_URL=$(jq -r '.api_url // ""' "$CONFIG_FILE")
|
||||
API_KEY=$(jq -r '.api_key // ""' "$CONFIG_FILE")
|
||||
MERCHANT_WALLET=$(jq -r '.merchant_wallet // ""' "$CONFIG_FILE")
|
||||
MERCHANT_CHAIN=$(jq -r '.merchant_chain // "solana"' "$CONFIG_FILE")
|
||||
DEFAULT_CHAIN=$(jq -r '.default_chain // "solana"' "$CONFIG_FILE")
|
||||
else API_URL=""; API_KEY=""; MERCHANT_WALLET=""; MERCHANT_CHAIN="solana"; DEFAULT_CHAIN="solana"
|
||||
fi
|
||||
}
|
||||
|
||||
save_config() {
|
||||
jq -n --arg api_url "$API_URL" --arg api_key "$API_KEY" --arg merchant_wallet "$MERCHANT_WALLET" --arg merchant_chain "$MERCHANT_CHAIN" --arg default_chain "$DEFAULT_CHAIN" \
|
||||
'{api_url: $api_url, api_key: $api_key, merchant_wallet: $merchant_wallet, merchant_chain: $merchant_chain, default_chain: $default_chain}' > "$CONFIG_FILE"
|
||||
ok "Configuration saved"
|
||||
}
|
||||
|
||||
# ─── API ─────────────────────────────────────────────────────────────
|
||||
|
||||
CV="/api/v1/chain-vault"
|
||||
WA="/api/v1/wallet"
|
||||
|
||||
api_call() {
|
||||
local method="$1" path="$2" body="${3:-}"
|
||||
if [[ -z "$API_URL" ]]; then die "API not configured. Run 'walletpress setup'"; fi
|
||||
local url="${API_URL}${path}" headers=()
|
||||
headers+=(-H "Content-Type: application/json")
|
||||
[[ -n "$API_KEY" ]] && headers+=(-H "Authorization: Bearer $API_KEY" -H "X-API-Key: $API_KEY")
|
||||
local cmd=("curl" "-sf")
|
||||
[[ -n "$body" ]] && cmd+=(-X "$method" -d "$body") || cmd+=(-X "$method")
|
||||
cmd+=( "${headers[@]}" "$url" )
|
||||
"${cmd[@]}" 2>/dev/null || return 1
|
||||
}
|
||||
|
||||
api_get() { api_call GET "$1" ""; }
|
||||
api_post() { api_call POST "$1" "$2"; }
|
||||
api_del() { api_call DELETE "$1" ""; }
|
||||
|
||||
# ─── Gates ───────────────────────────────────────────────────────────
|
||||
|
||||
load_gates() {
|
||||
if [[ -f "$GATES_FILE" ]] && [[ -s "$GATES_FILE" ]]; then GATES=$(cat "$GATES_FILE"); else GATES='[]'; fi
|
||||
}
|
||||
save_gates() { echo "$GATES" > "$GATES_FILE"; }
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# COMMANDS
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
cmd_setup() {
|
||||
hr; echo -e " ${BOLD}WalletPress Setup Wizard${NC}"; hr
|
||||
echo -e "\n${CYAN}[1/5] API Connection${NC}"
|
||||
read -rp " Backend API URL [${API_URL:-}]: " input; API_URL="${input:-$API_URL}"
|
||||
read -rsp " API Key: " input; echo; API_KEY="${input:-$API_KEY}"
|
||||
if [[ -n "$API_URL" && -n "$API_KEY" ]]; then
|
||||
info "Testing connection..."
|
||||
if health=$(api_get "/health" 2>/dev/null); then ok "Connected: $(echo "$health" | jq -r '.status // "ok"')"; else warn "API unreachable"; fi
|
||||
fi
|
||||
echo -e "\n${CYAN}[2/5] Merchant Wallet${NC}"
|
||||
read -rp " Wallet address [${MERCHANT_WALLET:-}]: " input; MERCHANT_WALLET="${input:-$MERCHANT_WALLET}"
|
||||
read -rp " Chain [${MERCHANT_CHAIN}]: " input; MERCHANT_CHAIN="${input:-$MERCHANT_CHAIN}"
|
||||
echo -e "\n${CYAN}[3/5] Default Chain${NC}"
|
||||
read -rp " Default chain [${DEFAULT_CHAIN}]: " input; DEFAULT_CHAIN="${input:-$DEFAULT_CHAIN}"
|
||||
save_config
|
||||
echo -e "\n${CYAN}[4/5] Generate First Wallet${NC}"
|
||||
read -rp " Generate a wallet now? (y/N): " gen
|
||||
if [[ "$gen" =~ ^[yY] ]]; then cmd_vault generate; fi
|
||||
echo -e "\n${CYAN}[5/5] Create Token Gate${NC}"
|
||||
read -rp " Create a token gate? (y/N): " gate
|
||||
if [[ "$gate" =~ ^[yY] ]]; then
|
||||
read -rp " Gate name: " title; read -rp " Token address: " token; read -rp " Chain [$DEFAULT_CHAIN]: " chain
|
||||
chain="${chain:-$DEFAULT_CHAIN}"; read -rp " Min amount [1]: " min; min="${min:-1}"
|
||||
load_gates; GATES=$(echo "$GATES" | jq --arg t "$title" --arg k "$token" --arg c "$chain" --argjson m "$min" '. += [{title: $t, token: $k, chain: $c, min_amount: $m, created_at: now|strflocaltime("%Y-%m-%dT%H:%M:%S")}]'); save_gates
|
||||
ok "Gate '$title' created"; fi
|
||||
hr; ok "Setup complete!"
|
||||
info "Run 'walletpress --help' to see all commands"
|
||||
}
|
||||
|
||||
cmd_dashboard() {
|
||||
load_config; hr
|
||||
echo -e " ${BOLD}WalletPress${NC} v${VERSION}"; hr
|
||||
echo -e " URL: ${API_URL:-(not set)}"
|
||||
echo -e " Merchant: ${MERCHANT_WALLET:0:16}... [${MERCHANT_CHAIN}]"
|
||||
echo -e " Default: ${DEFAULT_CHAIN}"
|
||||
if [[ -n "$API_URL" ]]; then
|
||||
health=$(api_get "/health" 2>/dev/null || echo '{"status":"unreachable"}')
|
||||
echo -e " API: $(echo "$health" | jq -r '.status')"
|
||||
stats=$(api_get "${CV}/stats" 2>/dev/null || echo '{}')
|
||||
echo -e " Vault: $(echo "$stats" | jq -r '.total_wallets // "?"') wallets"
|
||||
chains=$(api_get "${CV}/chains" 2>/dev/null || echo '[]')
|
||||
echo -e " Chains: $(echo "$chains" | jq 'length') supported"
|
||||
fi
|
||||
load_gates; local gc=$(echo "$GATES" | jq 'length')
|
||||
echo -e " Gates: $gc"
|
||||
hr
|
||||
}
|
||||
|
||||
# ─── VAULT ───────────────────────────────────────────────────────────
|
||||
|
||||
cmd_vault() {
|
||||
load_config
|
||||
case "${1:-list}" in
|
||||
list|ls)
|
||||
local vault
|
||||
vault=$(api_get "${CV}/vault" 2>/dev/null || die "Failed to fetch vault")
|
||||
local count=$(echo "$vault" | jq 'length - 1')
|
||||
echo -e "${BOLD}Vault (${count} wallets)${NC}"
|
||||
echo "$vault" | jq -r 'to_entries | .[] | select(.key != "_meta") | " \(.key[0:8]).. \(.value.chain // "?") \(.value.address // .value.public_key // "")[0:24]... \(.value.label // "")"'
|
||||
;;
|
||||
get|view)
|
||||
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Wallet ID: " id
|
||||
api_get "${CV}/vault/${id}" | jq '.' || warn "Wallet not found"
|
||||
;;
|
||||
generate|gen)
|
||||
local chain="${2:-$DEFAULT_CHAIN}" label="${3:-}"
|
||||
[[ -z "$chain" ]] && read -rp "Chain [$DEFAULT_CHAIN]: " chain; chain="${chain:-$DEFAULT_CHAIN}"
|
||||
[[ -z "$label" ]] && read -rp "Label: " label
|
||||
result=$(api_post "${CV}/generate" "{\"chain\":\"$chain\",\"label\":\"$label\"}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Wallet generated"; echo "$result" | jq '{chain, address, label}'; else die "Generation failed"; fi
|
||||
;;
|
||||
batch)
|
||||
local chains="${2:-solana,ethereum,base,polygon,bsc}" prefix="${3:-batch}"
|
||||
info "Generating wallets for: $chains"
|
||||
result=$(api_post "${CV}/generate/batch" "{\"chains\":\"$chains\",\"label_prefix\":\"$prefix\"}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Batch complete"; echo "$result" | jq '.'; else die "Batch failed"; fi
|
||||
;;
|
||||
all)
|
||||
local prefix="${2:-full}"
|
||||
info "Generating wallet for ALL supported chains..."
|
||||
result=$(api_post "${CV}/generate/batch" "{\"label_prefix\":\"$prefix\"}" || true)
|
||||
if [[ -n "$result" ]]; then ok "All chains generated"; echo "$result" | jq 'keys | .[]'; else die "Failed"; fi
|
||||
;;
|
||||
import)
|
||||
local chain="${2:-}" key="${3:-}" label="${4:-}"
|
||||
[[ -z "$chain" ]] && read -rp "Chain: " chain
|
||||
[[ -z "$key" ]] && read -rsp "Private key: " key; echo
|
||||
[[ -z "$label" ]] && read -rp "Label: " label
|
||||
result=$(api_post "${CV}/import" "{\"chain\":\"$chain\",\"private_key\":\"$key\",\"label\":\"$label\"}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Wallet imported"; echo "$result" | jq '.'; else die "Import failed"; fi
|
||||
;;
|
||||
stats)
|
||||
api_get "${CV}/stats" | jq '.' || warn "Failed to fetch stats"
|
||||
;;
|
||||
tree)
|
||||
api_get "${CV}/tree" | jq '.' || warn "Failed"
|
||||
;;
|
||||
health-score)
|
||||
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Wallet ID: " id
|
||||
api_get "${CV}/health-score/${id}" | jq '.' || warn "Failed"
|
||||
;;
|
||||
rotate)
|
||||
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Wallet ID: " id
|
||||
result=$(api_post "${CV}/rotate" "{\"wallet_id\":\"$id\"}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Wallet rotated"; echo "$result" | jq '{chain, new_address}'; else die "Rotation failed"; fi
|
||||
;;
|
||||
export)
|
||||
local format="${2:-json}"
|
||||
result=$(api_post "${CV}/export" "{\"format\":\"$format\"}" || true)
|
||||
if [[ -n "$result" ]]; then echo "$result" | jq '.'; else die "Export failed"; fi
|
||||
;;
|
||||
filter)
|
||||
local field="${2:-chain}" value="${3:-solana}"
|
||||
api_post "${CV}/bulk/filter" "{\"filters\":{\"$field\":\"$value\"}}" | jq '.' || warn "Filter failed"
|
||||
;;
|
||||
delete)
|
||||
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Wallet ID: " id
|
||||
result=$(api_post "${CV}/bulk/delete" "{\"ids\":[\"$id\"]}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Wallet deleted"; else die "Delete failed"; fi
|
||||
;;
|
||||
paper)
|
||||
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Wallet ID: " id
|
||||
api_get "${CV}/paper-wallet/${id}" | jq '.' || warn "Failed"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: walletpress vault <list|get|generate|batch|all|import|stats|tree|health-score|rotate|export|filter|delete|paper>"
|
||||
esac
|
||||
}
|
||||
|
||||
# ─── MNEMONIC ────────────────────────────────────────────────────────
|
||||
|
||||
cmd_mnemonic() {
|
||||
load_config
|
||||
local phrase="${1:-}" passphrase="${2:-}"
|
||||
if [[ -z "$phrase" ]]; then
|
||||
read -rp "Mnemonic phrase (12/24 words): " phrase
|
||||
read -rp "Passphrase (optional): " passphrase
|
||||
fi
|
||||
info "Deriving addresses for all chains..."
|
||||
result=$(api_post "${CV}/from-mnemonic" "{\"mnemonic\":\"$phrase\",\"passphrase\":\"$passphrase\"}" || true)
|
||||
if [[ -z "$result" ]]; then die "Conversion failed"; fi
|
||||
echo "$result" | jq -r '.wallets // .result // . | to_entries | .[] | " \(.key): \(.value.address // .value.public_key // "?")"'
|
||||
ok "Done — $(echo "$result" | jq '.wallets // .result // . | length') addresses derived"
|
||||
}
|
||||
|
||||
# ─── RECOVERY ────────────────────────────────────────────────────────
|
||||
|
||||
cmd_recover() {
|
||||
load_config
|
||||
local partial="${1:-}" chain="${2:-$DEFAULT_CHAIN}"
|
||||
if [[ -z "$partial" ]]; then
|
||||
read -rp "Partial mnemonic (use ? for unknown words): " partial
|
||||
read -rp "Chain [$chain]: " input; chain="${input:-$chain}"
|
||||
fi
|
||||
info "Attempting recovery on $chain..."
|
||||
info "This may take time for large partial phrases."
|
||||
# wallet_killer.py has the recovery engine — invoke via backend
|
||||
warn "Recovery endpoint not yet exposed in REST API. Use backend directly: python3 -c 'from wallet_killer import recover; print(recover(...))'"
|
||||
}
|
||||
|
||||
# ─── SWEEP ───────────────────────────────────────────────────────────
|
||||
|
||||
cmd_sweep() {
|
||||
load_config
|
||||
case "${1:-}" in
|
||||
rotate)
|
||||
local id="${2:-}" to="${3:-}"
|
||||
[[ -z "$id" ]] && read -rp "Wallet ID to rotate: " id
|
||||
[[ -z "$to" ]] && read -rp "Destination address: " to
|
||||
result=$(api_post "${CV}/rotate-sweep" "{\"wallet_id\":\"$id\",\"to_address\":\"$to\"}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Rotated and swept"; echo "$result" | jq '.'; else die "Failed"; fi
|
||||
;;
|
||||
dust|sweep)
|
||||
local id="${2:-}" to="${3:-}"
|
||||
[[ -z "$id" ]] && read -rp "Wallet ID to sweep: " id
|
||||
[[ -z "$to" ]] && read -rp "Destination address: " to
|
||||
result=$(api_post "${CV}/sweep" "{\"from_wallet_id\":\"$id\",\"to_address\":\"$to\"}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Swept"; echo "$result" | jq '.'; else die "Sweep failed"; fi
|
||||
;;
|
||||
temporal)
|
||||
local chain="${2:-$DEFAULT_CHAIN}" ttl="${3:-3600}" label="${4:-temporal}"
|
||||
result=$(api_post "${CV}/temporal/create" "{\"chain\":\"$chain\",\"ttl_seconds\":$ttl,\"label\":\"$label\"}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Temporal wallet created (TTL: ${ttl}s)"; echo "$result" | jq '{address, expires_at}'; else die "Failed"; fi
|
||||
;;
|
||||
temporal-list)
|
||||
api_get "${CV}/temporal/list" | jq '.' || warn "None"
|
||||
;;
|
||||
temporal-release)
|
||||
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Temporal ID: " id
|
||||
result=$(api_post "${CV}/temporal/release" "{\"temporal_id\":\"$id\"}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Released"; else die "Failed"; fi
|
||||
;;
|
||||
distribute)
|
||||
echo "Distribute funds across wallets (advanced). Use backend API directly for now."
|
||||
;;
|
||||
funding)
|
||||
local chain="${2:-$DEFAULT_CHAIN}" id="${3:-}" amount="${4:-}"
|
||||
[[ -z "$id" ]] && read -rp "Wallet ID: " id
|
||||
[[ -z "$amount" ]] && read -rp "Amount: " amount
|
||||
result=$(api_post "${CV}/funding-bundle" "{\"chain\":\"$chain\",\"wallet_id\":\"$id\",\"amount\":$amount}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Funding bundle created"; echo "$result" | jq '.'; else die "Failed"; fi
|
||||
;;
|
||||
deployer)
|
||||
local chain="${2:-$DEFAULT_CHAIN}" id="${3:-}"
|
||||
[[ -z "$id" ]] && read -rp "Wallet ID: " id
|
||||
result=$(api_post "${CV}/deployer-setup" "{\"chain\":\"$chain\",\"wallet_id\":\"$id\"}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Deployer setup done"; echo "$result" | jq '.'; else die "Failed"; fi
|
||||
;;
|
||||
*)
|
||||
echo "Usage: walletpress sweep <rotate|dust|temporal|temporal-list|temporal-release|funding|deployer>"
|
||||
esac
|
||||
}
|
||||
|
||||
# ─── ESCROW ──────────────────────────────────────────────────────────
|
||||
|
||||
cmd_escrow() {
|
||||
load_config
|
||||
case "${1:-create}" in
|
||||
create)
|
||||
local addr amount platform conditions
|
||||
read -rp "Deposit address: " addr
|
||||
read -rp "Amount (USD): " amount
|
||||
read -rp "Platform [solana]: " platform; platform="${platform:-solana}"
|
||||
read -rp "Conditions (JSON): " conditions
|
||||
result=$(api_post "${CV}/escrow" "{\"deposit_address\":\"$addr\",\"amount\":$amount,\"platform\":\"$platform\",\"conditions\":$conditions}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Escrow created"; echo "$result" | jq '.'; else die "Failed"; fi
|
||||
;;
|
||||
release)
|
||||
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Escrow ID: " id
|
||||
result=$(api_post "${CV}/escrow/release" "{\"escrow_id\":\"$id\"}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Released"; echo "$result" | jq '.'; else die "Failed"; fi
|
||||
;;
|
||||
*) echo "Usage: walletpress escrow <create|release>"
|
||||
esac
|
||||
}
|
||||
|
||||
# ─── WALLET ANALYSIS ────────────────────────────────────────────────
|
||||
|
||||
cmd_wallet() {
|
||||
load_config
|
||||
local sub="${1:-balance}" addr="${2:-}" chain="${3:-$DEFAULT_CHAIN}"
|
||||
[[ -z "$addr" ]] && read -rp "Address: " addr
|
||||
[[ "$sub" != "verify" ]] && [[ -z "$chain" || "$chain" == "$DEFAULT_CHAIN" ]] && read -rp "Chain [$DEFAULT_CHAIN]: " input && chain="${input:-$chain}"
|
||||
case "$sub" in
|
||||
balance)
|
||||
api_get "${WA}/${addr}/balance?chain=${chain}" | jq '.' || warn "Failed"
|
||||
;;
|
||||
analyze)
|
||||
api_get "${WA}/${addr}/analyze?chain=${chain}" | jq '.' || warn "Failed"
|
||||
;;
|
||||
scan)
|
||||
local tier="${3:-free}"
|
||||
api_post "${WA}/scan" "{\"address\":\"$addr\",\"chain\":\"$chain\",\"tier\":\"$tier\"}" | jq '.' || warn "Failed"
|
||||
;;
|
||||
txs|transactions)
|
||||
local limit="${4:-20}"
|
||||
api_get "${WA}/${addr}/transactions?chain=${chain}&limit=${limit}" | jq '.' || warn "Failed"
|
||||
;;
|
||||
verify)
|
||||
local msg sig
|
||||
read -rp "Message: " msg; read -rp "Signature: " sig
|
||||
api_post "${WA}/verify" "{\"address\":\"$addr\",\"message\":\"$msg\",\"signature\":\"$sig\",\"chain\":\"$chain\"}" | jq '.' || warn "Failed"
|
||||
;;
|
||||
*) echo "Usage: walletpress wallet <balance|analyze|scan|txs|verify> [address] [chain]"
|
||||
esac
|
||||
}
|
||||
|
||||
# ─── GATES ───────────────────────────────────────────────────────────
|
||||
|
||||
cmd_gate() {
|
||||
load_config; load_gates
|
||||
case "${1:-list}" in
|
||||
add)
|
||||
local title token chain min
|
||||
read -rp "Gate name: " title; read -rp "Token address: " token
|
||||
read -rp "Chain [$DEFAULT_CHAIN]: " chain; chain="${chain:-$DEFAULT_CHAIN}"
|
||||
read -rp "Min amount [1]: " min; min="${min:-1}"
|
||||
GATES=$(echo "$GATES" | jq --arg t "$title" --arg k "$token" --arg c "$chain" --argjson m "$min" '. += [{title: $t, token: $k, chain: $c, min_amount: $m, created_at: now|strflocaltime("%Y-%m-%dT%H:%M:%S")}]')
|
||||
save_gates; local id=$(echo "$GATES" | jq 'length - 1')
|
||||
ok "Gate '$title' created — shortcode: [wallet_gate id=\"$id\"]"
|
||||
;;
|
||||
list)
|
||||
local c=$(echo "$GATES" | jq 'length'); [[ "$c" -eq 0 ]] && { info "No gates"; return; }
|
||||
hr; echo -e "${BOLD}Token Gates (${c})${NC}"; hr
|
||||
echo "$GATES" | jq -r 'to_entries | .[] | " \(.key). \(.value.title)\n token: \(.value.token[0:32])... chain: \(.value.chain) min: \(.value.min_amount)\n code: [wallet_gate id=\"\(.key)\"]\n"'
|
||||
hr
|
||||
;;
|
||||
delete)
|
||||
local id="${2:-}"; [[ -z "$id" ]] && cmd_gate list >&2 && read -rp "Gate ID to delete: " id
|
||||
GATES=$(echo "$GATES" | jq "del(.[$id])"); save_gates; ok "Deleted"
|
||||
;;
|
||||
check)
|
||||
local addr="${2:-}" contract="${3:-}" chain="${4:-$DEFAULT_CHAIN}"
|
||||
[[ -z "$addr" ]] && read -rp "Wallet address: " addr
|
||||
[[ -z "$contract" ]] && read -rp "Contract address: " contract
|
||||
result=$(api_get "${WA}/${addr}/balance?chain=${chain}" 2>/dev/null || echo '{}')
|
||||
local held=$(echo "$result" | jq -r --arg c "$contract" '[.tokens // []] | map(select(.address == $c or .mint == $c)) | .[0].amount // 0')
|
||||
if [[ "$(echo "$held > 0" | bc -l 2>/dev/null)" == "1" ]]; then ok "Wallet holds $held of $contract"; else warn "No holdings found"; fi
|
||||
;;
|
||||
*) echo "Usage: walletpress gate <add|list|delete|check>"
|
||||
esac
|
||||
}
|
||||
|
||||
# ─── PAYMENTS ─────────────────────────────────────────────────────────
|
||||
|
||||
cmd_payment() {
|
||||
load_config
|
||||
case "${1:-list}" in
|
||||
create)
|
||||
local from to amount token chain
|
||||
read -rp "From address: " from; read -rp "To [${MERCHANT_WALLET:-}]: " to; to="${to:-$MERCHANT_WALLET}"
|
||||
read -rp "Amount: " amount; read -rp "Token [SOL]: " token; token="${token:-SOL}"
|
||||
read -rp "Chain [$DEFAULT_CHAIN]: " chain; chain="${chain:-$DEFAULT_CHAIN}"
|
||||
result=$(api_post "${CV}/tx/build" "{\"chain\":\"$chain\",\"from\":\"$from\",\"to\":\"$to\",\"amount\":$amount,\"token\":\"$token\"}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Payment built"; echo "$result" | jq '.'; else die "Build failed"; fi
|
||||
;;
|
||||
broadcast)
|
||||
local tx="${2:-}" chain="${3:-$DEFAULT_CHAIN}"
|
||||
[[ -z "$tx" ]] && read -rp "Signed TX (hex): " tx
|
||||
result=$(api_post "${CV}/tx/broadcast" "{\"chain\":\"$chain\",\"signed_tx\":\"$tx\"}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Broadcasted"; echo "$result" | jq '{tx_hash, status}'; else die "Failed"; fi
|
||||
;;
|
||||
list)
|
||||
echo "On-chain payments: use 'walletpress wallet txs <address>' to view"
|
||||
;;
|
||||
*) echo "Usage: walletpress payment <create|broadcast|list>"
|
||||
esac
|
||||
}
|
||||
|
||||
# ─── API KEYS ────────────────────────────────────────────────────────
|
||||
|
||||
cmd_key() {
|
||||
load_config
|
||||
case "${1:-generate}" in
|
||||
generate|create)
|
||||
local label="${2:-}" scopes="${3:-vault.read,generate}"
|
||||
[[ -z "$label" ]] && read -rp "Key label: " label
|
||||
result=$(api_post "${CV}/api-keys" "{\"label\":\"$label\",\"scopes\":[\"${scopes//,/\"\,\"}\"]}" || true)
|
||||
if [[ -n "$result" ]]; then
|
||||
hr; echo -e "${BOLD}New API Key${NC}"; hr
|
||||
echo -e " ${CYAN}$(echo "$result" | jq -r '.api_key // "generated"')${NC}"; hr
|
||||
echo " Save this now — it won't be shown again."
|
||||
else die "Key generation failed"; fi
|
||||
;;
|
||||
list)
|
||||
api_get "${CV}/api-keys" | jq '.' || info "No keys"
|
||||
;;
|
||||
revoke)
|
||||
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Key ID: " id
|
||||
result=$(api_post "${CV}/api-keys/revoke" "{\"key_id\":\"$id\"}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Revoked"; else die "Failed"; fi
|
||||
;;
|
||||
local)
|
||||
local key="wp_$(openssl rand -hex 24)"
|
||||
hr; echo -e "${BOLD}Local API Key${NC}"; hr
|
||||
echo -e " ${CYAN}$key${NC}"; hr
|
||||
echo " Add to backend .env: WALLETPRESS_API_KEY=$key"
|
||||
echo " Add to WordPress settings as your API key"
|
||||
;;
|
||||
*) echo "Usage: walletpress key <generate|list|revoke|local>"
|
||||
esac
|
||||
}
|
||||
|
||||
# ─── WEBHOOKS ────────────────────────────────────────────────────────
|
||||
|
||||
cmd_webhook() {
|
||||
load_config
|
||||
case "${1:-list}" in
|
||||
create)
|
||||
local url events secret
|
||||
read -rp "Webhook URL: " url
|
||||
read -rp "Events (comma-sep) [wallet.generated]: " events; events="${events:-wallet.generated}"
|
||||
read -rp "Secret: " secret
|
||||
result=$(api_post "${CV}/webhooks" "{\"url\":\"$url\",\"events\":[\"${events//,/\"\,\"}\"],\"secret\":\"$secret\"}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Webhook created"; else die "Failed"; fi
|
||||
;;
|
||||
list)
|
||||
api_get "${CV}/webhooks" | jq '.' || info "No webhooks"
|
||||
;;
|
||||
delete)
|
||||
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Webhook ID: " id
|
||||
api_del "${CV}/webhooks/${id}" && ok "Deleted" || die "Failed"
|
||||
;;
|
||||
*) echo "Usage: walletpress webhook <create|list|delete>"
|
||||
esac
|
||||
}
|
||||
|
||||
# ─── BALANCES ────────────────────────────────────────────────────────
|
||||
|
||||
cmd_balances() {
|
||||
load_config
|
||||
case "${1:-list}" in
|
||||
list)
|
||||
api_get "${CV}/balances" | jq '.' || warn "Failed"
|
||||
;;
|
||||
snapshot)
|
||||
api_post "${CV}/balances/snapshot" "{}" | jq '.' || warn "Failed"
|
||||
;;
|
||||
history)
|
||||
local days="${2:-30}"
|
||||
api_get "${CV}/balances/history?days=${days}" | jq '.' || warn "Failed"
|
||||
;;
|
||||
*) echo "Usage: walletpress balances <list|snapshot|history>"
|
||||
esac
|
||||
}
|
||||
|
||||
# ─── CHAINS ──────────────────────────────────────────────────────────
|
||||
|
||||
cmd_chain() {
|
||||
load_config
|
||||
case "${1:-list}" in
|
||||
list)
|
||||
api_get "${CV}/chains" | jq -r 'to_entries | .[] | " \(.key): \(.value.name // .value)"' || warn "Failed"
|
||||
;;
|
||||
rpc)
|
||||
api_get "${CV}/rpc-chains" | jq '.' || warn "Failed"
|
||||
;;
|
||||
validate)
|
||||
local chain="${2:-}" addr="${3:-}"
|
||||
[[ -z "$chain" ]] && read -rp "Chain: " chain
|
||||
[[ -z "$addr" ]] && read -rp "Address: " addr
|
||||
api_get "${CV}/validate/${chain}/${addr}" | jq '.' || warn "Failed"
|
||||
;;
|
||||
link-proof)
|
||||
local src="${2:-}" sc="${3:-}" tc="${4:-}"
|
||||
[[ -z "$src" ]] && read -rp "Source address: " src
|
||||
[[ -z "$sc" ]] && read -rp "Source chain: " sc
|
||||
[[ -z "$tc" ]] && read -rp "Target chain: " tc
|
||||
api_post "${CV}/link/proof" "{\"source_address\":\"$src\",\"source_chain\":\"$sc\",\"target_chain\":\"$tc\"}" | jq '.' || warn "Failed"
|
||||
;;
|
||||
link-verify)
|
||||
local addr="${2:-}" chain="${3:-}" proof="${4:-}"
|
||||
[[ -z "$addr" ]] && read -rp "Address: " addr
|
||||
[[ -z "$chain" ]] && read -rp "Chain: " chain
|
||||
[[ -z "$proof" ]] && read -rp "Proof: " proof
|
||||
api_post "${CV}/link/verify" "{\"address\":\"$addr\",\"chain\":\"$chain\",\"proof\":\"$proof\"}" | jq '.' || warn "Failed"
|
||||
;;
|
||||
*) echo "Usage: walletpress chain <list|rpc|validate|link-proof|link-verify>"
|
||||
esac
|
||||
}
|
||||
|
||||
# ─── AUDIT ───────────────────────────────────────────────────────────
|
||||
|
||||
cmd_audit() {
|
||||
load_config
|
||||
local limit="${1:-50}"
|
||||
api_get "${CV}/audit-trail?limit=${limit}" | jq '.' || warn "No audit entries"
|
||||
}
|
||||
|
||||
# ─── CLUSTER ─────────────────────────────────────────────────────────
|
||||
|
||||
cmd_cluster() {
|
||||
load_config
|
||||
local chain="${1:-$DEFAULT_CHAIN}" count="${2:-10}"
|
||||
info "Generating cluster-aware wallets on $chain..."
|
||||
result=$(api_post "${CV}/cluster" "{\"chain\":\"$chain\",\"count\":$count}" || true)
|
||||
if [[ -n "$result" ]]; then ok "Cluster generated"; echo "$result" | jq '.'; else die "Failed"; fi
|
||||
}
|
||||
|
||||
# ─── HEALTH ──────────────────────────────────────────────────────────
|
||||
|
||||
cmd_health() {
|
||||
load_config; hr
|
||||
echo -e "${BOLD}WalletPress Health Check${NC}"; hr
|
||||
echo -n " Config: "; [[ -f "$CONFIG_FILE" ]] && ok "found" || warn "not configured"
|
||||
echo -n " API: "
|
||||
if [[ -n "$API_URL" ]]; then
|
||||
local h; h=$(api_get "/health" 2>/dev/null || true)
|
||||
[[ -n "$h" ]] && ok "$(echo "$h" | jq -r '.status')" || warn "unreachable"
|
||||
else warn "not configured"; fi
|
||||
echo -n " Vault: "
|
||||
local v; v=$(api_get "${CV}/stats" 2>/dev/null || true)
|
||||
[[ -n "$v" ]] && ok "$(echo "$v" | jq -r '.total_wallets // 0') wallets" || warn "unknown"
|
||||
echo -n " Chains: "
|
||||
local c; c=$(api_get "${CV}/chains" 2>/dev/null || true)
|
||||
[[ -n "$c" ]] && ok "$(echo "$c" | jq 'length') supported" || warn "unknown"
|
||||
echo -n " Merchant: "
|
||||
[[ -n "$MERCHANT_WALLET" ]] && ok "${MERCHANT_WALLET:0:8}... [$MERCHANT_CHAIN]" || warn "not set"
|
||||
load_gates; echo -n " Gates: "; ok "$(echo "$GATES" | jq 'length') gate(s)"
|
||||
hr
|
||||
}
|
||||
|
||||
# ─── SERVE ───────────────────────────────────────────────────────────
|
||||
|
||||
cmd_serve() {
|
||||
load_config; local port="${1:-8580}" host="${2:-127.0.0.1}"
|
||||
command -v python3 &>/dev/null || die "python3 required"
|
||||
command -v walletpress-backend &>/dev/null && {
|
||||
info "Starting WalletPress backend on $host:$port"
|
||||
walletpress-backend serve --host "$host" --port "$port"
|
||||
return
|
||||
}
|
||||
command -v uvicorn &>/dev/null && {
|
||||
info "Starting WalletPress API at http://$host:$port"
|
||||
cd "$(dirname "$0")/../backend" 2>/dev/null && uvicorn main:app --host "$host" --port "$port" --reload
|
||||
return
|
||||
}
|
||||
info "Admin UI at http://$host:$port — Ctrl+C to stop"; hr
|
||||
cd "$DATA_DIR" && python3 -m http.server "$port" --bind "$host"
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# MAIN
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
load_config
|
||||
|
||||
case "${1:-help}" in
|
||||
help|-h|--help) cat <<'HELP'
|
||||
WalletPress CLI — self-hosted multi-chain wallet generator
|
||||
|
||||
Usage: walletpress <command> [options]
|
||||
|
||||
Commands:
|
||||
setup Run setup wizard
|
||||
dash Show dashboard
|
||||
vault List/browse vault wallets
|
||||
mnemonic Convert mnemonic to addresses
|
||||
recover Attempt wallet recovery
|
||||
sweep Sweep & rotate wallets
|
||||
escrow Create escrow wallets
|
||||
wallet Wallet operations
|
||||
gate Token gate management
|
||||
payment Payment records
|
||||
serve Start admin UI
|
||||
help Show this help
|
||||
HELP
|
||||
;;
|
||||
setup|wizard) shift; cmd_setup "$@" ;;
|
||||
dash|dashboard|st) cmd_dashboard ;;
|
||||
vault|wallet-vault) shift; cmd_vault "$@" ;;
|
||||
mnemonic|convert) shift; cmd_mnemonic "$@" ;;
|
||||
recover|recovery) shift; cmd_recover "$@" ;;
|
||||
sweep) shift; cmd_sweep "$@" ;;
|
||||
escrow) shift; cmd_escrow "$@" ;;
|
||||
wallet|address) shift; cmd_wallet "$@" ;;
|
||||
gate|gates) shift; cmd_gate "$@" ;;
|
||||
pay|payment) shift; cmd_payment "$@" ;;
|
||||
key|keys|apikey) shift; cmd_key "$@" ;;
|
||||
webhook|hooks) shift; cmd_webhook "$@" ;;
|
||||
balance|balances) shift; cmd_balances "$@" ;;
|
||||
chain|chains) shift; cmd_chain "$@" ;;
|
||||
audit|logs) shift; cmd_audit "$@" ;;
|
||||
cluster) shift; cmd_cluster "$@" ;;
|
||||
health|status) cmd_health ;;
|
||||
serve|ui) shift; cmd_serve "$@" ;;
|
||||
version|-v|--version) echo "WalletPress CLI v${VERSION}" ;;
|
||||
help|-h|--help|"")
|
||||
hr
|
||||
echo -e "${BOLD}WalletPress CLI v${VERSION}${NC}"; hr
|
||||
echo -e " ${CYAN}setup${NC} Interactive setup wizard (5 steps)"
|
||||
echo -e " ${CYAN}dashboard${NC} System overview"
|
||||
echo -e " ${CYAN}vault${NC} Wallet vault: list, get, generate, batch, all, import,"; echo " stats, tree, health-score, rotate, export, filter, delete, paper"
|
||||
echo -e " ${CYAN}mnemonic${NC} BIP39 mnemonic → 55 chain addresses"
|
||||
echo -e " ${CYAN}recover${NC} Wallet recovery from partial mnemonics"
|
||||
echo -e " ${CYAN}sweep${NC} Sweep: rotate, dust, temporal, funding, deployer"
|
||||
echo -e " ${CYAN}escrow${NC} Pay-to-release escrow: create, release"
|
||||
echo -e " ${CYAN}wallet${NC} Wallet analysis: balance, analyze, scan, txs, verify"
|
||||
echo -e " ${CYAN}gate${NC} Token gates: add, list, delete, check"
|
||||
echo -e " ${CYAN}payment${NC} Crypto payments: create, broadcast"
|
||||
echo -e " ${CYAN}key${NC} API keys: generate, list, revoke, local"
|
||||
echo -e " ${CYAN}webhook${NC} Webhooks: create, list, delete"
|
||||
echo -e " ${CYAN}balances${NC} Balance tracking: list, snapshot, history"
|
||||
echo -e " ${CYAN}chain${NC} Chain info: list, rpc, validate, link-proof, link-verify"
|
||||
echo -e " ${CYAN}audit${NC} Audit trail viewer"
|
||||
echo -e " ${CYAN}cluster${NC} Cluster-aware wallet generation"
|
||||
echo -e " ${CYAN}health${NC} System health check"
|
||||
echo -e " ${CYAN}serve${NC} Start admin HTTP UI [port] [host]"
|
||||
hr
|
||||
echo " Config: $CONFIG_FILE | Data: $DATA_DIR | Log: $LOG_FILE"
|
||||
hr
|
||||
;;
|
||||
*) die "Unknown: $1. Run 'walletpress help'"
|
||||
esac
|
||||
Loading…
Add table
Add a link
Reference in a new issue