---
name: weavers-staking
description: Stake, unstake, and claim SPDR rewards for Web Weavers NFTs through the public Weavers API at https://weavers.carbium.io. Use when the user wants to stake/unstake a Weaver, claim staking rewards, or check what they have staked. The flow is always: authenticate the wallet, ask the API to BUILD an unsigned transaction, sign it with the user's wallet, then SUBMIT it back. The server never holds the user's key.
---

# Weavers Staking

Stake Web Weavers NFTs to earn SPDR rewards, via the public HTTP API at
`https://weavers.carbium.io`. All write actions follow one pattern:

```
authenticate -> POST /<action>/build -> sign the returned tx -> POST /tx/submit
```

The API returns a partially-built transaction (the server co-signs only its own
delegate slot when required; usually it is single-signer). The user's wallet signs
the owner slot. You then relay the signed wire transaction back through
`/tx/submit`. The server never sees or holds the user's private key.

## On-chain constants

| Name | Value |
| --- | --- |
| Program ID | `ww3st35u7nDFdAypP8nXjyCQy9cUMp9i1cuAJq2JKgL` |
| Collection | `44drPeDtf1tLwRXjRYZdNm7ZHX3tQytrYgvdbGjfbADC` |
| Reward mint (SPDR) | `AT79ReYU9XtHUTF5vM6Q4oa9K8w7918Fp5SU7G1MDMQY` (9 decimals) |
| API base | `https://weavers.carbium.io` |

## 1. Authenticate (wallet JWT)

Two requests: get a challenge, sign its `message` with the wallet (ed25519, this is
a plain message signature, no funds move), and verify to receive a bearer token.

```bash
BASE=https://weavers.carbium.io

# a) get a challenge
CH=$(curl -s -X POST $BASE/auth/wallet/challenge)
# CH -> {"challengeId":"...","message":"weavers wallet auth\nid:...\nts:...\nSign to prove wallet control. No funds move."}

# b) sign CH.message with the wallet -> base64(signature)
# c) verify
curl -s -X POST $BASE/auth/wallet/verify \
  -H 'content-type: application/json' \
  -d '{"challengeId":"<challengeId>","wallet":"<OWNER_PUBKEY>","signature":"<BASE64_SIG>"}'
# -> {"token":"<JWT>","expiresIn":"30d"}
```

Send the token as `Authorization: Bearer <JWT>` on every build/submit call. The
`owner` in each request body must equal the wallet that authenticated.

## 2. Build -> sign -> submit (the core loop)

```js
const BASE = "https://weavers.carbium.io";

// 1) build (server returns a base64 wire tx, or {txs:[...]} for multi-tx batches)
const { tx } = await fetch(`${BASE}/stake/build`, {
  method: "POST",
  headers: { "content-type": "application/json", authorization: `Bearer ${jwt}` },
  body: JSON.stringify({ owner, mint, period: "604800" }),
}).then((r) => r.json());

// 2) sign the wire tx with the user's wallet (deserialize -> partialSign owner ->
//    re-serialize to base64). Do NOT strip the server's existing signature.

// 3) submit the fully-signed wire tx
const res = await fetch(`${BASE}/tx/submit`, {
  method: "POST",
  headers: { "content-type": "application/json", authorization: `Bearer ${jwt}` },
  body: JSON.stringify({ tx: signedWireTxBase64, owner }),
}).then((r) => r.json());
// res -> { txId: "<signature>", success: true }
```

When a build returns `{ txs: [...] }` (a batch too large for one transaction), sign
and submit each entry in order.

## Actions

All require the `Bearer` token. `owner` = the authenticated wallet (base58).

### Stake

`POST /stake/build`: body `{ owner, mint, period }`.
- `mint`: the Weaver NFT address to stake.
- `period`: lock duration in **seconds**, as a string. Must match an on-chain lock
  tier. Longer locks earn a higher reward multiplier.
- Returns `{ tx }`.

`POST /stake/bulk-build`: body `{ owner, mints: string[], period }`. Stakes many in
the fewest transactions. Returns `{ tx }` or `{ txs: [...] }`.

### Unstake (only after the lock period has elapsed)

`POST /unstake/build`: body `{ owner, mint }`. Returns `{ tx }`.

`POST /unstake/bulk-build`: body `{ owner, mints: string[] }`. Returns
`{ txs: [...], skipped: [...] }`; `skipped` lists requested mints that were not
staked by the owner.

### Claim SPDR

`POST /claim/build`: body `{ owner }` to claim everything, or `{ owner, mint }` to
claim one asset. Returns `{ tx }` or `{ txs: [...] }`.

### Read what is staked / owned

`GET /api/nfts/owner/<wallet>` (Bearer token; wallet must match). Returns
`{ assets: [{ mint, staked, frozen, name, imageUrl, attributes }] }`.

## Notes

- All endpoints are rate limited (per minute, per IP). The auth + build endpoints
  sit around 30-90 requests/min; submit is lower. Back off on HTTP 429.
- A failed submit returns HTTP 502 `{ error, correlationId }`. Surface the
  correlationId; never retry blindly.
- Unstake is rejected on-chain until the lock period has fully elapsed.
- This skill is user-scoped. It never needs, asks for, or stores a private key;
  signing happens in the user's own wallet.
