Skip to content

004-02: Stake2Pay Plugin (House of Stake NEAR Staking) - #36

Merged
jaswinder6991 merged 1 commit into
mainfrom
004-02-stake2pay-plugin
Jul 14, 2026
Merged

jaswinder6991 merged 1 commit into
mainfrom
004-02-stake2pay-plugin

Conversation

@jaswinder6991

Copy link
Copy Markdown
Contributor

Closes #29. Part of Epic 004 (#27). Depends on #35 (merged).

What

plugins/stake2pay/ now fully implements SubscriptionContract against the House of Stake staking contract — a TypeScript port of the chain-facing half of nearai/chat-api PR #275, adapted to the aggregator's wallet-intent model. No secrets: all chain access is read-only JSON-RPC view calls; the user's wallet does all writes. Registered config-only in bos.config.json (plugins.stake2pay, no secrets array).

How it maps chain state

  • listPlans ← get_product + get_price views, filtered to Active+Recurring, with a 60s in-memory catalog cache. Range plans: minAmount = price amount, maxAmount = metadata.max_amount.
  • getSubscription ← get_subscription_for_price + get_lock(last_lock_id) (never cached). Status derivation: chain Active within the period maps to active/cancel_at_period_end; otherwise the lock decides — not-yet-withdrawn → pending_unstake, withdrawn/absent → ended; null → none. Deferred plan changes (pending_update) surface in metadata.
  • createSubscription → multi-action wallet_intent: auto-includes storage_deposit when the payer is unregistered (or when the contract's per_lock_storage_stake requires a top-up, read from get_config), then lock with {price_id, duration_ns: null} and the chosen stake as deposit. Stake validated against the plan range via BigInt before the wallet ever opens.
  • cancel / resume → 1-yocto cancel_subscription/resume_subscription intents addressed by product_id (the contract keys lifecycle ops by product, not price — resolved from the live subscription).
  • changePlan → update_subscription intent attaching the stake delta for increases (computed from the current lock) or 1 yocto for decreases (which the contract defers to the billing boundary).

RPC failures and timeouts surface as typed oRPC errors (SERVICE_UNAVAILABLE), unknown/non-recurring plans as NOT_FOUND, out-of-range amounts as BAD_REQUEST — mapped from tagged Effect errors via Exit/Cause, no message sniffing.

Verification

  • 58 tests green: 27 schema (from 004-01) + 24 unit (fixture chain JSON through a fake RPC client — status matrix, storage top-up variants, delta math, error tags) + 7 live read-only integration tests against hos-e2e-0601144939.testnet (skippable with SKIP_HOS_TESTNET=1; CI doesn't run plugin tests).
  • Standalone dev server verified: bun run dev boots and GET /api/subscriptions/plans returns the seeded testnet tiers with correct NEAR ranges (Starter 1–10, Basic 10–40, Pro 40–400).
  • bun run typecheck and biome check clean on all changed files.

Chain shapes were verified against the live testnet contract (get_product, get_price, get_subscription_for_price, storage_balance_bounds, get_config) and the feat/stake-dao branch's staking-contract/docs/API.md + types.rs, not just the chat-api port.

Still manual (per ticket)

One wallet-signed lock against testnet to confirm end-to-end signability — planned alongside the 004-04 UI work, since the intent consumer is the UI's near-kit signer.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FdKukDnRTrshKqkQ8JXwVj

…-02)

Implements SubscriptionContract against the HoS staking contract via
read-only JSON-RPC view calls; the wallet does all writes, so the plugin
has no secrets (config-only registration in bos.config.json).

- client.ts: minimal fetch-based view-call helper (base64 args, 15s
  timeout, typed error messages)
- chain.ts: Zod schemas for on-chain JSON (product, price, subscription,
  lock, config, storage balance)
- service.ts: chain -> contract mapping; listPlans (60s catalog cache,
  filters to active recurring prices), getSubscription (status derived
  from subscription + lock state: pending_unstake vs ended), wallet
  intents for lock / cancel_subscription / resume_subscription /
  update_subscription with storage_deposit auto-included when needed,
  range validation via BigInt
- index.ts: Exit/Cause-based mapping of tagged service errors to typed
  oRPC errors (NOT_FOUND / BAD_REQUEST / SERVICE_UNAVAILABLE)
- tests: 27 schema + 24 service unit tests (fixture chain JSON, fake RPC
  client) and 7 live read-only integration tests against
  hos-e2e-0601144939.testnet (skippable via SKIP_HOS_TESTNET=1)

Closes #29

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdKukDnRTrshKqkQ8JXwVj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

004-02: Stake2Pay Plugin (House of Stake NEAR Staking)

1 participant