sparkbtcbot-proxy

ClawSkills 作者 clawskills

Use a Spark Bitcoin L2 wallet proxy for AI agents via HTTP API. Check balances, send payments, create invoices, pay L402 paywalls — all without holding the mnemonic. Use when user mentions "Spark proxy," "wallet API," "L402," "proxy payment," "bearer token auth," or wants secured Bitcoin capabilities for an agent.

安装 / 下载方式

TotalClaw CLI推荐
totalclaw install clawskills:clawskills~echennells-sparkbtcbot-proxy
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/clawskills%3Aclawskills~echennells-sparkbtcbot-proxy/file -o echennells-sparkbtcbot-proxy.md
# Spark Bitcoin L2 Proxy for AI Agents

You are an expert in using the sparkbtcbot-proxy — a serverless HTTP API that gives AI agents scoped access to a Spark Bitcoin L2 wallet without exposing the private key.

## Why Use the Proxy Instead of Direct SDK

| Concern | Direct SDK (sparkbtcbot-skill) | Proxy (this skill) |
|---------|-------------------------------|-------------------|
| Mnemonic location | Agent holds it | Server holds it |
| Spending limits | None (agent decides) | Per-tx and daily caps |
| Access revocation | Move funds to new wallet | Revoke bearer token |
| Role-based access | No | Yes (admin, invoice, pay-only, read-only) |
| Setup complexity | npm install + mnemonic | HTTP calls + bearer token |

**Use the proxy when:**
- You don't trust the agent with full wallet control
- You need spending limits or audit logs
- You want to revoke access without moving funds
- Multiple agents share one wallet with different permissions

**Use direct SDK when:**
- Testing or development
- Agent needs offline signing
- You're building the proxy itself

## Before You Start

1. **Deploy your own proxy** — see [sparkbtcbot-proxy](https://github.com/echennells/sparkbtcbot-proxy) for setup instructions. The proxy runs on Vercel (free tier works) with Upstash Redis.

2. **Use HTTPS only** — never connect to a proxy over plain HTTP. All Vercel deployments use HTTPS by default.

3. **Create least-privilege tokens** — don't give agents admin tokens. Use the most restrictive role that works:
   - `read-only` for monitoring/dashboard agents
   - `invoice` for agents that receive payments but don't spend
   - `pay-only` for agents that pay L402 paywalls but don't create invoices
   - `admin` only for your own management scripts

4. **Set spending limits** — configure `maxTxSats` and `dailyBudgetSats` when creating tokens. The proxy enforces these server-side.

5. **Test with small amounts** — start with a few hundred sats until you trust your agent's behavior.

6. **Have a revocation plan** — know how to revoke tokens via `DELETE /api/tokens` if an agent is compromised.

## Token Roles

| Role | Permissions |
|------|-------------|
| `admin` | Full access: read, create invoices, pay, transfer, manage tokens |
| `invoice` | Read + create invoices. Cannot pay or transfer. |
| `pay-only` | Read + pay invoices and L402. Cannot create invoices or transfer. |
| `read-only` | Read only (balance, info, transactions, logs). Cannot pay or create invoices. |

## Base URL

The proxy runs on Vercel. Your base URL will look like:
```
https://your-deployment.vercel.app
```

All requests require authentication:
```
Authorization: Bearer <your-token>
```

## API Reference

### Read Operations (any role)

#### Get Balance

```bash
curl -H "Authorization: Bearer $TOKEN" \
  "$PROXY_URL/api/balance"
```

Response:
```json
{
  "success": true,
  "data": {
    "balance": "50000",
    "tokenBalances": {
      "btkn1...": {
        "balance": "1000",
        "tokenMetadata": {
          "tokenName": "Example Token",
          "tokenTicker": "EXT",
          "decimals": 0
        }
      }
    }
  }
}
```

#### Get Wallet Info

```bash
curl -H "Authorization: Bearer $TOKEN" \
  "$PROXY_URL/api/info"
```

Response:
```json
{
  "success": true,
  "data": {
    "sparkAddress": "sp1p...",
    "identityPublicKey": "02abc..."
  }
}
```

#### Get Deposit Address (L1 Bitcoin)

```bash
curl -H "Authorization: Bearer $TOKEN" \
  "$PROXY_URL/api/deposit-address"
```

Response:
```json
{
  "success": true,
  "data": {
    "address": "bc1p..."
  }
}
```

#### Get Transaction History

```bash
curl -H "Authorization: Bearer $TOKEN" \
  "$PROXY_URL/api/transactions?limit=10&offset=0"
```

#### Get Fee Estimate

```bash
curl -H "Authorization: Bearer $TOKEN" \
  "$PROXY_URL/api/fee-estimate?invoice=lnbc..."
```

Response:
```json
{
  "success": true,
  "data": {
    "feeSats": 5
  }
}
```

#### Get Activity Logs

```bash
curl -H "Authorization: Bearer $TOKEN" \
  "$PROXY_URL/api/logs?limit=20"
```

### Invoice Operations (admin or invoice role)

#### Create Lightning Invoice (BOLT11)

```bash
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"amountSats": 1000, "memo": "Payment for service", "expirySeconds": 3600}' \
  "$PROXY_URL/api/invoice/create"
```

Response:
```json
{
  "success": true,
  "data": {
    "encodedInvoice": "lnbc10u1p..."
  }
}
```

#### Create Spark Invoice

```bash
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"amount": 1000, "memo": "Spark payment"}' \
  "$PROXY_URL/api/invoice/spark"
```

### Payment Operations (admin or pay-only role)

#### Pay Lightning Invoice

```bash
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"invoice": "lnbc10u1p...", "maxFeeSats": 10}' \
  "$PROXY_URL/api/pay"
```

Response:
```json
{
  "success": true,
  "data": {
    "id": "payment-id-123",
    "status": "LIGHTNING_PAYMENT_SUCCEEDED",
    "paymentPreimage": "abc123..."
  }
}
```

#### Transfer to Spark Address

```bash
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"receiverSparkAddress": "sp1p...", "amountSats": 1000}' \
  "$PROXY_URL/api/transfer"
```

### L402 Paywall Operations (admin or pay-only role)

L402 lets you pay for API access with Lightning. The proxy handles the full flow automatically.

#### Pay L402 and Fetch Content

```bash
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://lightningfaucet.com/api/l402/joke", "maxFeeSats": 50}' \
  "$PROXY_URL/api/l402"
```

Response (immediate success):
```json
{
  "success": true,
  "data": {
    "status": 200,
    "paid": true,
    "priceSats": 21,
    "preimage": "be2ebe7c...",
    "data": {"setup": "Why do programmers...", "punchline": "..."}
  }
}
```

Response (cached token reused):
```json
{
  "success": true,
  "data": {
    "status": 200,
    "paid": false,
    "cached": true,
    "data": {"setup": "...", "punchline": "..."}
  }
}
```

#### Preview L402 Cost (any role)

Check what an L402 resource costs without paying:

```bash
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://lightningfaucet.com/api/l402/joke"}' \
  "$PROXY_URL/api/l402/preview"
```

Response:
```json
{
  "success": true,
  "data": {
    "requires_payment": true,
    "invoice_amount_sats": 21,
    "invoice": "lnbc210n1p...",
    "macaroon": "AgELbGlnaHRuaW5n..."
  }
}
```

#### Handling Pending L402 Payments (IMPORTANT)

Lightning payments are asynchronous. If the preimage isn't available within ~7.5 seconds, the proxy returns a pending status:

```json
{
  "success": true,
  "data": {
    "status": "pending",
    "pendingId": "a1b2c3d4...",
    "message": "Payment sent but preimage not yet available. Poll GET /api/l402/status?id=<pendingId> to complete.",
    "priceSats": 21
  }
}
```

**You MUST handle this case.** The payment has been sent — if you don't poll, you lose sats without getting content.

Poll for completion:

```bash
curl -H "Authorization: Bearer $TOKEN" \
  "$PROXY_URL/api/l402/status?id=a1b2c3d4..."
```

**Recommended retry logic:**

```javascript
async function fetchL402(proxyUrl, token, targetUrl, maxFeeSats = 50) {
  const response = await fetch(`${proxyUrl}/api/l402`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ url: targetUrl, maxFeeSats }),
  });

  const result = await response.json();

  if (result.data?.status === 'pending') {
    const pendingId = result.data.pendingId;
    for (let i = 0; i < 10; i++) {
      await new Promise(r => setTimeout(r, 3000));
      const statusResponse = await fetch(
        `${proxyUrl}/api/l402/status?id=${pendingId}`,
        { headers: { 'Authorization': `Bearer ${token}` } }
      );