# Conventions Source: https://docs.hubra.app/developer/conventions Request shapes, response shapes, error format, and the small rules that apply across all endpoints. These are the cross-cutting rules. Read them once; they apply to every endpoint. *** ## Base URL ``` https://hubra.app/api/v1 ``` *** ## Content type Every request and successful response uses `application/json`. ```http theme={null} Content-Type: application/json ``` Errors use `application/problem+json` ([RFC 9457](https://www.rfc-editor.org/rfc/rfc9457)). See [Errors](/developer/errors) for the format. *** ## Decimal amounts All amounts are passed and returned as **decimal strings**, not floats. ```json theme={null} { "amount": "1.5" } ``` ```json theme={null} { "outAmount": "1.7234" } ``` This avoids IEEE-754 drift and matches the precision the on-chain stake program enforces. Numeric strings are parsed by the server with full precision; floats are not safe. The on-chain math itself happens in lamports (1 SOL = 10⁹ lamports) or USDC's smallest unit (10⁶), but you never need to do that conversion: pass `"1.5"`, get back `"1.7234"`, and the server handles the unit math. *** ## Versioning Every response includes: ```http theme={null} X-Hubra-Api-Version: v1 ``` Breaking changes ship as `/api/v2`. The `v1` surface is committed to API stability for the duration of its deployed life. *** ## CORS Read endpoints (`GET`) accept any origin: ```http theme={null} Access-Control-Allow-Origin: * Access-Control-Allow-Headers: Authorization, Content-Type, Idempotency-Key Access-Control-Allow-Methods: GET, POST, OPTIONS ``` Write endpoints accept the same. Tighter origin policy may be applied per-route in future versions. Preflights (`OPTIONS`) return `204` with the same headers and an empty body. *** ## HTTP methods | Method | Used for | | --------- | ------------------------------------------------------------------ | | `GET` | Public reads (strategies, APY history, health) | | `POST` | All writes; also any read that takes a request body (e.g., quotes) | | `OPTIONS` | CORS preflight | There is no `PUT`, `PATCH`, or `DELETE` in the v1 surface. *** ## Caching Read endpoints are `force-dynamic` server-side (no Next.js caching of the response). Upstream data (Sanctum APY, validator APY, Voltr stats) has its own cache windows; expect those to refresh on the order of a few minutes. If you need the freshest possible APY, hit `/api/v1/strategies` directly rather than reading from a cached client. *** ## Error format Errors follow [RFC 9457 problem-details](https://www.rfc-editor.org/rfc/rfc9457): ```json theme={null} { "type": "https://hubra.app/errors/invalid_request", "title": "Invalid request", "status": 400, "detail": "Required fields: strategy, wallet, amount." } ``` Branch on `type`, not on `title`. The `type` slug is stable; the `title` is human-friendly and may evolve. See [Errors](/developer/errors) for the full slug list. *** ## Rate limiting There is currently no rate limit on the v1 surface. As the API matures, per-IP soft limits will be applied. When that ships, responses will include `RateLimit-*` headers ([RFC 9331](https://www.rfc-editor.org/rfc/rfc9331)). For high-volume callers, contact [hello@hubra.app](mailto:hello@hubra.app) to discuss. *** ## Idempotency Write endpoints accept `Idempotency-Key: ` and replay the same response for that key within a 24-hour window. This matters for agent retries, where a network blip might cause a double-build of the same transaction. ```bash theme={null} curl -X POST https://hubra.app/api/v1/stake \ -H 'Content-Type: application/json' \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"strategy":"sol-native-stake","wallet":"","amount":"1.0"}' ``` *** ## Authentication There is none on the v1 surface. The on-chain signature on the actual stake transaction is the only authorization that matters. Any caller can build an unsigned transaction; only the wallet that owns the input asset can sign it. If a future version adds API keys, the auth header will be: ```http theme={null} Authorization: Bearer hbr_live_<22 base58> ``` For now, the header is unused. *** ## Referral attribution A referral code on a stake call credits the staking wallet to the code's owner. This is **not** authentication — it's attribution, and it's entirely optional. Hubra **partners** (protocols, devs, creators) get a code from [partner.hubra.app](https://partner.hubra.app) that resolves to their payout wallet, but any referrer's code works — a partner is just a referrer. Pass it on [`POST /api/v1/stake`](/developer/endpoints/stake#referral-attribution), either as a `referral_code` body field or an `X-Referral-Code` header: ```http theme={null} X-Referral-Code: HUBAB23 ``` The first referrer to bring a given wallet in keeps the credit (first-write-wins), and a wallet is never credited to itself. A malformed code returns `400 invalid_request`; one that doesn't resolve returns `404 not_found`. *** ## What's next The strategy registry. Problem-details and how to branch on slug. The HMAC gate on /broadcast. Solana / staking terminology. # GET /apy/history Source: https://docs.hubra.app/developer/endpoints/apy-history Time-series APY for any live strategy. ```http theme={null} GET https://hubra.app/api/v1/apy/history?strategy={key}&range={range} ``` Time-series APY for any live strategy. Backed by the same upstreams the human chart reads: * **Sanctum** for `sol-liquid-stake` (raSOL). * **thevalidators.io** for `sol-native-stake` (Hubra validator). * **rasol-max daily-cron snapshot** for `sol-leveraged-stake` (per-epoch points; the same series the leverage page's bar chart shows). * **Voltr** for `usdc-earn`. `sol-leveraged-stake` only writes one row per epoch (\~12 points retained), and the upstream doesn't slice into 1M / 3M / 6M windows the way Sanctum and thevalidators.io do — the same series is returned under every `range` so the response shape stays consistent across strategies. *** ## Request ```bash theme={null} curl "https://hubra.app/api/v1/apy/history?strategy=sol-liquid-stake&range=3M" ``` | Query parameter | Type | Required | Description | | --------------- | ------------------------------- | -------- | --------------------------- | | `strategy` | `string` | yes | Canonical strategy key. | | `range` | `"1M" \| "3M" \| "6M" \| "All"` | no | Time range. Default `"1M"`. | *** ## Response ```json theme={null} { "strategy": "sol-liquid-stake", "range": "3M", "points": [ { "date": "2026-02-07", "apy": 5.78 }, { "date": "2026-02-14", "apy": 5.82 }, { "date": "2026-02-21", "apy": 5.91 }, { "date": "2026-02-28", "apy": 6.04 } ] } ``` | Field | Type | Description | | --------------- | ----------------- | -------------------------------------------- | | `strategy` | `string` | Echoed strategy key. | | `range` | `string` | Echoed range (`"1M"` if not specified). | | `points` | `{ date, apy }[]` | Time-series points, ordered oldest → newest. | | `points[].date` | `string` | ISO 8601 date. | | `points[].apy` | `number` | APY as a percentage. | The number of points returned depends on the range and the upstream's resolution. `"All"` returns the full series the upstream provides. *** ## Errors | Status | Slug | When | | ------ | --------------------- | ----------------------------------------------------------------------------------------------------------- | | `400` | `invalid_request` | Missing `strategy` or invalid `range`. | | `404` | `not_found` | Unknown strategy key. | | `502` | `upstream_error` | Upstream provider could not return history (Sanctum / thevalidators.io / Voltr unreachable or unparseable). | | `503` | `service_unavailable` | Strategy is announced but not live; no APY history yet. | Example invalid range: ```json theme={null} { "type": "https://hubra.app/errors/invalid_request", "title": "Invalid request", "status": 400, "detail": "Invalid range \"BAD\". Expected one of: 1M, 3M, 6M, All." } ``` *** ## When to call * Rendering a chart to the user. * Computing rolling averages or comparing strategies. * Auditing performance of a position over time. For the latest single APY number (not a series), call [`GET /api/v1/strategies/{key}`](/developer/endpoints/get-strategy) instead — it is a single round-trip and always fresh. # POST /broadcast Source: https://docs.hubra.app/developer/endpoints/broadcast Submit a fully signed transaction. Default route is plain Solana RPC; Sanctum is opt-in for swap flows. ```http theme={null} POST https://hubra.app/api/v1/broadcast ``` Submit a fully signed transaction. The default route is plain Solana RPC, which works for any signed transaction (Sanctum-built, Voltr-built, native). The chain does not care which builder produced the bytes. Optional `route: "sanctum"` (with `sanctumKind` and `sanctum_order`) forwards to Sanctum's execute endpoint, which adds MEV protection and smarter retries. Use it when broadcasting transactions that came back from `/stake` or `/unstake` with `route: "sanctum"`. `hubra_token` is **required** on every call. Without a matching token, the endpoint rejects the request with `403 forbidden`. *** ## Request ```bash theme={null} curl -X POST https://hubra.app/api/v1/broadcast \ -H 'Content-Type: application/json' \ -d '{ "signed_tx": "", "hubra_token": "" }' ``` | Field | Type | Required | Description | | --------------- | -------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `signed_tx` | `string` | yes | Base64-encoded fully signed transaction. | | `hubra_token` | `string` | yes | Token from the matching `/stake` or `/unstake` response. HMAC over the unsigned message bytes. Tokens expire \~2 minutes after issue. | | `route` | `"rpc" \| "sanctum"` | optional | Defaults to `"rpc"`. Use `"sanctum"` for Sanctum-routed swap flows. | | `sanctumKind` | `"token" \| "depositStake" \| "depositSol" \| "withdrawStake"` | conditional | Required when `route="sanctum"`. Pass through the value from the `/stake` or `/unstake` response. | | `sanctum_order` | `object` | conditional | Required when `route="sanctum"`. Pass through the `sanctum_order` object from the `/stake` or `/unstake` response. Sanctum's execute endpoint validates the signed transaction against this. | *** ## Response ```json theme={null} { "signature": "5z6Z...", "explorer": "https://solscan.io/tx/5z6Z..." } ``` | Field | Type | Description | | ----------- | -------- | ----------------------------------------- | | `signature` | `string` | Base58 on-chain transaction signature. | | `explorer` | `string` | Pre-built Solscan link for the signature. | *** ## When to use Sanctum routing | You came from | Use | | -------------------------------------- | ------------------ | | `/stake sol-liquid-stake` | `route: "sanctum"` | | `/stake sol-native-stake` | `route: "rpc"` | | `/stake usdc-earn` | `route: "rpc"` | | `/unstake sol-native-stake instant` | `route: "sanctum"` | | `/unstake sol-native-stake deactivate` | `route: "rpc"` | | `/unstake sol-liquid-stake instant` | `route: "sanctum"` | | `/unstake sol-liquid-stake slow` | `route: "sanctum"` | | `/unstake usdc-earn instant` | `route: "rpc"` | | `/withdraw` | `route: "rpc"` | The simple rule: if the build response had `route: "sanctum"`, broadcast with `route: "sanctum"` and forward the Sanctum-specific fields. Sanctum's broadcaster adds MEV protection and smarter retries. Plain RPC works for any signed transaction; the chain itself does not distinguish. *** ## Signing example (Node + `@solana/web3.js`) ```ts theme={null} import { VersionedTransaction, Keypair } from "@solana/web3.js"; // 1. Build the unsigned tx const buildResp = await fetch("https://hubra.app/api/v1/stake", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ strategy: "sol-liquid-stake", wallet: walletPubkey, amount: "1.0", }), }).then((r) => r.json()); const { transaction, hubra_token, route, sanctumKind, sanctum_order } = buildResp; // 2. Sign locally const tx = VersionedTransaction.deserialize(Buffer.from(transaction, "base64")); tx.sign([wallet]); // wallet is a Keypair you hold const signed = Buffer.from(tx.serialize()).toString("base64"); // 3. Broadcast — forward Sanctum-specific fields when applicable const broadcastBody = route === "sanctum" ? { signed_tx: signed, hubra_token, route, sanctumKind, sanctum_order } : { signed_tx: signed, hubra_token }; const { signature } = await fetch("https://hubra.app/api/v1/broadcast", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(broadcastBody), }).then((r) => r.json()); console.log(`Confirmed: https://solscan.io/tx/${signature}`); ``` For `sol-native-stake`, `tx.sign([wallet])` only fills the wallet's slot. The stake-account slot is pre-signed by Hubra's server. *** ## Errors | Status | Slug | When | | ------ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | `400` | `invalid_request` | Missing `signed_tx`, missing `hubra_token`, missing `sanctum_order` (when `route="sanctum"`), or invalid `route` / `sanctumKind`. | | `403` | `forbidden` | `hubra_token` does not match this transaction, is malformed, or has expired. Rebuild via `/stake` or `/unstake` to get a fresh token. | | `502` | `upstream_error` | RPC or Sanctum rejected the broadcast (invalid blockhash, signature failure, simulation failure, insufficient funds). | *** ## See also The HMAC mechanism in detail. Build a stake transaction. Build an unstake transaction. Problem-details and slugs. # GET /strategies/:key Source: https://docs.hubra.app/developer/endpoints/get-strategy Per-strategy detail: intro, ordered steps, trust labels, on-chain handles, and live numbers. ```http theme={null} GET https://hubra.app/api/v1/strategies/{key} ``` Full per-strategy context: intro paragraph, ordered steps, trust labels, on-chain handles, and the action kinds the agent surface accepts. Live APY, exchange rate, and any other live data are included alongside the static descriptors so a single call answers "what is this strategy and what is it doing right now?" *** ## Request ```bash theme={null} curl https://hubra.app/api/v1/strategies/sol-liquid-stake ``` | Path parameter | Type | Description | | -------------- | -------- | --------------------------------------------------------------------------------------------------------------- | | `key` | `string` | One of the canonical strategy keys: `sol-native-stake`, `sol-liquid-stake`, `sol-leveraged-stake`, `usdc-earn`. | *** ## Response (live strategy) ```json theme={null} { "strategy": { "key": "sol-liquid-stake", "asset": "SOL", "title": "Liquid", "blurb": "Mint raSOL via Sanctum.", "status": "live", "intro": "Mint raSOL by depositing SOL into Hubra's Sanctum-routed pool. Your raSOL is non-rebasing; the SOL redemption rate climbs as the underlying stake earns rewards.", "steps": [ { "ord": "1", "lead": "Deposit SOL", "tail": "Routed to Hubra's validator via Sanctum." }, { "ord": "2", "lead": "Receive raSOL", "tail": "Non-rebasing, value-accruing receipt." }, { "ord": "3", "lead": "Use freely", "tail": "Hold, swap, lend, or LP across Solana DeFi." } ], "trust": [ "Self-custody", "No Hubra protocol fee", "Sanctum-audited infrastructure" ], "onchain": { "assetMint": "So11111111111111111111111111111111111111112", "receiptMint": "HUBsveNpjo5pWqNkH57QzxjQASdTVXcSK7bVKTSZtcSX", "receiptSymbol": "raSOL" }, "actions": { "stake": { "kinds": ["mint"], "notes": "Mints raSOL via Sanctum's swap router (SOL → raSOL)." }, "unstake": { "kinds": ["instant", "slow"], "notes": "`instant` routes raSOL → SOL via Sanctum liquidity. `slow` runs Sanctum withdrawStake to a native stake account, then standard epoch deactivation." } }, "live": { "apy": 6.4, "exchangeRate": 1.0723 } } } ``` ### Field reference | Field | Description | | ----------------------- | ------------------------------------------------------------------------------------------------------- | | `intro` | One-paragraph summary an agent can quote to its principal. | | `steps` | Numbered steps describing what the strategy does, in order. | | `trust` | Trust labels — the same chips the UI shows. | | `onchain` | On-chain handles relevant to building transactions against this strategy. Field set varies by strategy. | | `actions.stake.kinds` | Action kinds accepted by `POST /api/v1/stake` for this strategy. | | `actions.unstake.kinds` | Action kinds accepted by `POST /api/v1/unstake` for this strategy. | | `live.apy` | Headline APY as a percentage. | | `live.exchangeRate` | Current asset-per-receipt rate. | The `onchain` field set is strategy-specific: * **`sol-native-stake`:** `validatorVoteAccount`, `assetMint`. * **`sol-liquid-stake`:** `assetMint`, `receiptMint`, `receiptSymbol`. * **`sol-leveraged-stake`:** `assetMint` (SOL), `depositMint` (raSOL — what the strategy ingests), `receiptMint` (raSOL Max LP), `receiptSymbol`. * **`usdc-earn`:** `assetMint`, `receiptMint`, `receiptSymbol`, `voltrVault`. For `sol-leveraged-stake`, `actions.stake.kinds` is `["deposit"]` and `actions.unstake.kinds` is `["instant"]` — both are agent-callable. Note `depositMint` (raSOL) is what the **vault** ingests; the `/api/v1/stake` endpoint itself deposits **SOL** and builds the SOL → raSOL leg for you. See [Strategies → `sol-leveraged-stake`](/developer/strategies#sol-leveraged-stake). *** ## Errors | Status | Slug | When | | ------ | --------------------- | -------------------------------------------------------------------------- | | `404` | `not_found` | Unknown strategy key. See `GET /api/v1/strategies` for the canonical list. | | `503` | `service_unavailable` | Strategy is announced but not live yet. Includes a `Retry-After` header. | Coming-soon example response: ```json theme={null} { "type": "https://hubra.app/errors/service_unavailable", "title": "Service unavailable", "status": 503, "detail": "Strategy \"\" is announced but not live yet." } ``` *** ## See also The full list. Build a stake tx using one of the strategy keys. # GET /health Source: https://docs.hubra.app/developer/endpoints/health Liveness probe. Confirms the API is wired and reports the deployed commit. ```http theme={null} GET https://hubra.app/api/v1/health ``` A cheap liveness probe. Confirms the API surface is wired and exposes the version and current commit so callers can tell which build they are talking to. This endpoint does not call any upstream provider; it is a single-digit-millisecond response. *** ## Request No parameters, no body, no auth. ```bash theme={null} curl https://hubra.app/api/v1/health ``` *** ## Response ```json theme={null} { "ok": true, "service": "hubra-agent-api", "version": "v1", "commit": "", "time": "2026-05-07T07:39:30.123Z" } ``` | Field | Type | Description | | --------- | --------- | -------------------------------------------------------------------------------------------------------------------- | | `ok` | `boolean` | Always `true` when this endpoint responds; if you cannot reach the API at all, you will get a network error instead. | | `service` | `string` | Always `"hubra-agent-api"`. | | `version` | `string` | API surface version. Always `"v1"` for this endpoint. | | `commit` | `string` | Deployed commit SHA; `"unknown"` in dev or when not injected by the platform. | | `time` | `string` | Server time, ISO 8601. | *** ## Use cases * **Smoke testing** before running a stake flow. * **Deploy verification** by comparing `commit` against your expected SHA. * **Clock drift checks** by comparing `time` against your local clock. *** ## Errors This endpoint does not return errors under normal conditions. If you see a non-2xx response, treat it as a network or platform-level outage rather than an API surface issue. # GET /strategies Source: https://docs.hubra.app/developer/endpoints/list-strategies List every canonical strategy with its live APY and exchange rate. ```http theme={null} GET https://hubra.app/api/v1/strategies ``` The agent's primary "what can I do here?" entry point. Returns every strategy the API knows about, with the same live numbers the human surface displays. *** ## Request No parameters, no body, no auth. ```bash theme={null} curl https://hubra.app/api/v1/strategies ``` *** ## Response ```json theme={null} { "strategies": [ { "key": "sol-native-stake", "asset": "SOL", "title": "Native", "blurb": "Delegate SOL to Hubra's validator.", "status": "live", "live": { "apy": 6.7, "exchangeRate": null } }, { "key": "sol-liquid-stake", "asset": "SOL", "title": "Liquid", "blurb": "Mint raSOL via Sanctum.", "status": "live", "live": { "apy": 6.4, "exchangeRate": 1.0723 } }, { "key": "sol-leveraged-stake", "asset": "SOL", "title": "Leveraged raSOL Max", "blurb": "Auto-managed leveraged raSOL. Amplified staking yield, no active management.", "status": "live", "live": { "apy": 9.93, "exchangeRate": 1.1988 } }, { "key": "usdc-earn", "asset": "USDC", "title": "Earn", "blurb": "Routed USDC vault.", "status": "live", "live": { "apy": 5.6, "exchangeRate": 1.0123 } } ] } ``` ### Item fields | Field | Type | Description | | ------------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | `key` | `string` | Canonical strategy key (`sol-native-stake`, `sol-liquid-stake`, `sol-leveraged-stake`, `usdc-earn`). Use this to address the strategy in other endpoints. | | `asset` | `"SOL" \| "USDC"` | The input asset. | | `title` | `string` | Short human-friendly name. Same string the UI shows. | | `blurb` | `string` | One-line description. | | `status` | `"live" \| "coming_soon"` | Whether the strategy is callable. | | `live.apy` | `number \| null` | Headline APY as a percentage (e.g. `6.4` for 6.4%), or `null` if upstream data is unavailable. | | `live.exchangeRate` | `number \| null` | Current asset-per-receipt rate. `null` for strategies without a receipt token (native stake). | *** ## When to call * Once at session start, to discover what is available. * Before showing live APY in a UI; the server-side cache window is a few minutes. * When the user is comparing strategies side-by-side. For static metadata (steps, trust labels, on-chain handles) and deeper detail on a single strategy, use [`GET /api/v1/strategies/{key}`](/developer/endpoints/get-strategy). *** ## Errors This endpoint does not return errors under normal conditions. If an upstream APY lookup fails, the corresponding `live.apy` falls back to `null`; the strategy entry itself is still returned. *** ## See also Per-strategy full detail. Time-series APY. # POST /quote Source: https://docs.hubra.app/developer/endpoints/quote Preview an unstake's output amount and price impact before signing. ```http theme={null} POST https://hubra.app/api/v1/quote ``` Preview the output of an **unstake** without committing. Use this to show price impact and output amount to a user before they sign anything. Quotes are **non-binding**: pool state drifts between quote and unstake. Treat the returned `outAmount` and `priceImpactPct` as live estimates. There is no quote endpoint for staking *into* a strategy. Stake quotes are deterministic: amount in equals amount out at the current receipt rate. Use [`GET /api/v1/strategies/{key}`](/developer/endpoints/get-strategy) to read the rate and compute locally. *** ## Request ```bash theme={null} curl -X POST https://hubra.app/api/v1/quote \ -H 'Content-Type: application/json' \ -d '{ "strategy": "sol-liquid-stake", "wallet": "", "amount": "1.5" }' ``` | Field | Type | Required | Description | | -------------- | ------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------- | | `strategy` | `"sol-liquid-stake" \| "sol-native-stake" \| "sol-leveraged-stake"` | yes | Which strategy to quote. | | `wallet` | `string` | yes | Solana wallet pubkey (base58). | | `amount` | `string` | yes | Amount to unstake, decimal string. For `sol-leveraged-stake`, this is the **raSOL Max LP** to burn. | | `stakeAccount` | `string` | conditional | Required for `sol-native-stake`: the active stake account being settled. Not used for `sol-liquid-stake` or `sol-leveraged-stake`. | *** ## Response — `sol-liquid-stake` ```json theme={null} { "strategy": "sol-liquid-stake", "inAsset": "raSOL", "outAsset": "SOL", "inAmount": "1.5", "outAmount": "1.7616", "priceImpactPct": 0.0048 } ``` | Field | Type | Description | | ---------------- | ---------------- | ---------------------------------------------------------------------------------------------------------- | | `inAsset` | `string` | Asset coming in (`raSOL`). | | `outAsset` | `string` | Asset going out (`SOL`). | | `inAmount` | `string` | Echoed input amount. | | `outAmount` | `string` | Estimated SOL out at current pool state. | | `priceImpactPct` | `number \| null` | Estimated price impact as a fraction (`0.0048` = 0.48%). `null` if the upstream router did not report one. | *** ## Response — `sol-native-stake` (instant) ```json theme={null} { "strategy": "sol-native-stake", "kind": "instant", "inAsset": "SOL", "outAsset": "SOL", "inAmount": "1.0", "outAmount": "0.9952", "priceImpactPct": 0.0048 } ``` Same shape as liquid; the input "asset" is SOL because the active stake account is being settled to SOL via Sanctum's `depositStake`. `kind: "instant"` indicates this is a Sanctum-routed instant unstake quote (the only kind that can be quoted; `deactivate` does not have liquidity-based pricing). *** ## Response — `sol-leveraged-stake` Previews burning raSOL Max LP back to raSOL. `amount` is the LP to burn; `path` is the redemption route the unstake would take. ```json theme={null} { "strategy": "sol-leveraged-stake", "inAsset": "raSOL Max", "outAsset": "raSOL", "inAmount": "0.0195", "outAmount": "0.019485797", "path": "flash-bracket", "kind": "instant" } ``` | Field | Type | Description | | ----------- | --------------------------------- | --------------------------------------------------------------- | | `inAsset` | `string` | `raSOL Max` (the LP being burned). | | `outAsset` | `string` | `raSOL` (the payout asset — the leveraged exit stops at raSOL). | | `outAmount` | `string` | Estimated raSOL out at current NAV. | | `path` | `"vault-idle" \| "flash-bracket"` | Which redemption path the unstake would use. | There is no leveraged **deposit** quote: the SOL → raSOL mint is a deterministic floor-division from the live pool snapshot, so there's nothing to preview. To estimate the SOL you'd ultimately receive (rather than raSOL), feed `outAmount` into a `sol-liquid-stake` quote. *** ## Errors | Status | Slug | When | | ------ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `400` | `invalid_request` | Missing `strategy` / `wallet` / `amount`, or `stakeAccount` missing for `sol-native-stake`. Also returned for `usdc-earn` (a direct vault redemption with no slippage — nothing to quote). | | `404` | `not_found` | Unknown strategy key. | | `502` | `upstream_error` | Sanctum's quote router could not produce a number (no liquidity, wallet has no on-chain history, etc.). | | `503` | `service_unavailable` | Strategy is announced but not live. | *** ## See also Build the actual unstake transaction. Read the current exchange rate. # POST /stake Source: https://docs.hubra.app/developer/endpoints/stake Build a Solana transaction that stakes the strategy's input asset. Returns base64 unsigned (or partially signed) bytes. ```http theme={null} POST https://hubra.app/api/v1/stake ``` Build a Solana transaction that stakes `amount` of the strategy's input asset. The returned `transaction` is **base64-encoded**: * **Unsigned** for `sol-liquid-stake`, `sol-leveraged-stake`, and `usdc-earn`. * **Partially signed** for `sol-native-stake` (the stake-account slot is pre-signed by the server; you only sign the wallet slot). The agent signs locally and broadcasts via [`POST /api/v1/broadcast`](/developer/endpoints/broadcast). *** ## Request ```bash theme={null} curl -X POST https://hubra.app/api/v1/stake \ -H 'Content-Type: application/json' \ -d '{ "strategy": "sol-liquid-stake", "wallet": "", "amount": "1.5" }' ``` | Field | Type | Required | Description | | --------------- | ---------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `strategy` | `"sol-native-stake" \| "sol-liquid-stake" \| "sol-leveraged-stake" \| "usdc-earn"` | yes | Which strategy to stake into. | | `wallet` | `string` | yes | Solana wallet pubkey (base58) that will sign the transaction. | | `amount` | `string` | yes | Amount to stake, decimal string. The input asset is the strategy's `asset` — **SOL** for the three `sol-*` keys (including `sol-leveraged-stake`), USDC for `usdc-earn`. | | `referral_code` | `string` | no | A referral code. Credits the staking wallet to the code's owner. Can also be sent as the `X-Referral-Code` header instead of in the body. See [Referral attribution](#referral-attribution). | *** ## Referral attribution Pass a `referral_code` on a stake call and the staking wallet is credited to the code's owner. This is Hubra's native referral model — the same relationship the web app records — so a wallet's `referred_by` is set to the code owner's wallet. If you're a Hubra **partner** (protocol, dev, or creator), your referral code from [partner.hubra.app](https://partner.hubra.app) resolves to your payout wallet, so wallets that stake with your code show up on your [partner dashboard](https://partner.hubra.app). But any referrer's code works — a partner is just a referrer. Send the code either as a body field or a header: ```bash theme={null} # As a body field curl -X POST https://hubra.app/api/v1/stake \ -H 'Content-Type: application/json' \ -d '{ "strategy": "sol-liquid-stake", "wallet": "", "amount": "1.5", "referral_code": "HUBAB23" }' # …or as a header (keeps the code out of the JSON body) curl -X POST https://hubra.app/api/v1/stake \ -H 'Content-Type: application/json' \ -H 'X-Referral-Code: HUBAB23' \ -d '{"strategy":"sol-liquid-stake","wallet":"","amount":"1.5"}' ``` Attribution is **first-write-wins** per wallet: the first referrer to bring a wallet in keeps the credit, and a wallet is never credited to itself. Passing a code is optional — omit it and the stake behaves exactly as before. A code is matched case-insensitively (`^[A-Z0-9]{4,12}$`); if you pass one that is malformed or does not resolve, the call returns `400 invalid_request` or `404 not_found` so you notice rather than silently losing attribution. *** ## Response — `sol-native-stake` The server generates a fresh stake-account keypair and pre-signs that signature slot. The agent only signs as the wallet. **Save the returned `stakeAccount` pubkey** — you need it for future deactivate / withdraw / instant-unstake calls against this position. ```json theme={null} { "strategy": "sol-native-stake", "transaction": "", "hubra_token": "", "stakeAccount": "", "voteAccount": "7K8DVxtNJGnMtUY1CQJT5jcs8sFGSZTDiG7kowvFpECh", "rentExemptReserveLamports": 2282880, "delegatedLamports": 1500000000, "signers": [""], "notes": "Stake account keypair was generated and pre-signed server-side." } ``` Broadcast via `route: "rpc"` (no Sanctum involvement on the create + delegate path). *** ## Response — `sol-liquid-stake` (Sanctum-routed) ```json theme={null} { "strategy": "sol-liquid-stake", "transaction": "", "hubra_token": "", "route": "sanctum", "sanctumKind": "token", "sanctum_order": { "...": "..." }, "signers": [""] } ``` When `route: "sanctum"`, the response also carries `sanctumKind` and `sanctum_order`. **All three (`hubra_token`, `sanctumKind`, `sanctum_order`) must be passed back to `/broadcast`** if you want Sanctum's MEV-protected broadcaster. Sanctum's execute endpoint validates the signed transaction against the original order and rejects mismatches. Plain RPC (`route: "rpc"` at broadcast) works without the Sanctum-specific fields, but loses Sanctum's broadcaster guarantees. *** ## Response — `usdc-earn` (Voltr-routed) ```json theme={null} { "strategy": "usdc-earn", "transaction": "", "hubra_token": "", "route": "voltr", "signers": [""] } ``` Voltr-routed responses do not include `sanctum_order`. Broadcast via `route: "rpc"` — that is the only supported broadcast path for Voltr transactions. *** ## Response — `sol-leveraged-stake` (raSOL Max) `amount` is **SOL**. The server builds a **single transaction** that does the whole conversion: create the raSOL ATA (idempotent) → SPL stake-pool `DepositSol` (SOL → raSOL) → Voltr `depositVault` (raSOL → raSOL Max LP). One signature, atomic. ```json theme={null} { "strategy": "sol-leveraged-stake", "transaction": "", "hubra_token": "", "route": "rpc", "depositedSolLamports": "20000000", "stagedRasolLamports": "16700000", "receiptMint": "CJEYakpjmKBvvUzAn3HJSs9vtijnv472T8YJEV3WzToF", "receiptSymbol": "raSOL Max", "signers": [""] } ``` Broadcast via `route: "rpc"` — no Sanctum or Voltr execute leg. After it confirms, read your raSOL Max LP balance on-chain; you pass that figure to [`POST /api/v1/unstake`](/developer/endpoints/unstake) to burn the position later. The API deposits **SOL only** — the SOL → raSOL leg is built in for you. There is no "deposit raSOL directly" variant on the agent surface. The Voltr leg is sized from the exact raSOL the stake-pool mint produces (the same floor-rounded `lamports × supply / total_lamports` the pool applies on-chain), so the staged raSOL always matches what the deposit instruction expects. *** ## The `hubra_token` Every `/stake` (and `/unstake`) response includes a `hubra_token` HMAC'd over the message bytes of the unsigned transaction. `POST /api/v1/broadcast` requires this token and rejects any transaction that was not built by Hubra. **Save it alongside the transaction and pass it back when broadcasting.** Tokens expire \~2 minutes after issue (matching Solana's blockhash window). If your token expires, rebuild via `/stake`. For the full mechanics, see [Hubra token](/developer/hubra-token). *** ## Examples ```bash theme={null} # Native delegation to Hubra's validator curl -X POST https://hubra.app/api/v1/stake \ -H 'Content-Type: application/json' \ -d '{"strategy":"sol-native-stake","wallet":"","amount":"1.0"}' # Liquid: SOL → raSOL via Sanctum curl -X POST https://hubra.app/api/v1/stake \ -H 'Content-Type: application/json' \ -d '{"strategy":"sol-liquid-stake","wallet":"","amount":"1.0"}' # Leveraged: SOL → raSOL → raSOL Max in one tx curl -X POST https://hubra.app/api/v1/stake \ -H 'Content-Type: application/json' \ -d '{"strategy":"sol-leveraged-stake","wallet":"","amount":"1.0"}' # USDC vault deposit curl -X POST https://hubra.app/api/v1/stake \ -H 'Content-Type: application/json' \ -d '{"strategy":"usdc-earn","wallet":"","amount":"100"}' ``` *** ## Errors | Status | Slug | When | | ------ | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | `400` | `invalid_request` | Missing or malformed fields, including a `referral_code` that isn't `^[A-Z0-9]{4,12}$`. | | `404` | `not_found` | Unknown strategy key, or a `referral_code` (or `X-Referral-Code`) that doesn't resolve to any referrer. | | `502` | `upstream_error` | Sanctum / Voltr could not build the transaction (wallet does not exist on-chain, no associated token account, insufficient liquidity for size). | | `503` | `service_unavailable` | Strategy is announced but not live. | *** ## See also Reverse the position. Submit the signed transaction. The HMAC gate. All strategy keys. # POST /unstake Source: https://docs.hubra.app/developer/endpoints/unstake Build an unsigned unstake transaction. Routes by strategy + kind. ```http theme={null} POST https://hubra.app/api/v1/unstake ``` Build an unsigned unstake transaction. The route depends on `strategy` and `kind`: | Strategy | `deactivate` | `instant` | `slow` | | --------------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | | `sol-native-stake` | `StakeProgram.deactivate` (epoch-bounded \~2.5d) | Sanctum `depositStake` (active stake → SOL immediately) | — | | `sol-liquid-stake` | — | Sanctum swap raSOL → SOL via routed liquidity | Sanctum `withdrawStake` (raSOL → native stake account, then standard epoch deactivation) | | `sol-leveraged-stake` | — | Burn raSOL Max LP → raSOL via the rasol-max SDK (vault-idle or MarginFi flash bracket) | — | | `usdc-earn` | — | Voltr direct-withdraw (no cooldown, no fee) | — | The returned `transaction` is base64 unsigned bytes. Sign and broadcast via [`POST /api/v1/broadcast`](/developer/endpoints/broadcast). *** ## Request ```bash theme={null} curl -X POST https://hubra.app/api/v1/unstake \ -H 'Content-Type: application/json' \ -d '{ "strategy": "sol-native-stake", "wallet": "", "stakeAccount": "", "kind": "deactivate" }' ``` | Field | Type | Required | Description | | --------------- | ------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | `strategy` | `string` | yes | One of `sol-native-stake`, `sol-liquid-stake`, `sol-leveraged-stake`, `usdc-earn`. | | `wallet` | `string` | yes | Solana wallet pubkey. | | `kind` | `"deactivate" \| "instant" \| "slow"` | yes | The unstake mode. See the matrix above for valid combinations. `sol-leveraged-stake` accepts only `instant`. | | `amount` | `string` | conditional | Required except for `usdc-earn` with `isWithdrawAll: true`. For `sol-leveraged-stake`, this is the **raSOL Max LP** to burn (decimal, 9 decimals). | | `stakeAccount` | `string` | conditional | Required for all `sol-native-stake` calls. The active stake account returned by your earlier `/stake` call. | | `isWithdrawAll` | `boolean` | optional | Valid only for `usdc-earn`. When `true`, withdraws the full vault position regardless of `amount`. | | `outputAsset` | `"raSOL" \| "SOL"` | optional | Valid only for `sol-leveraged-stake` (default `"raSOL"`). `"SOL"` attaches a `next` step that converts the resulting raSOL → SOL. See below. | *** ## Response shapes The response shape mirrors `/stake`: `transaction` (base64 unsigned), `hubra_token` (required at `/broadcast`), plus route metadata. ### Sanctum-routed kinds For `sol-native-stake instant`, `sol-liquid-stake instant`, and `sol-liquid-stake slow`: ```json theme={null} { "strategy": "sol-liquid-stake", "kind": "instant", "transaction": "", "hubra_token": "", "route": "sanctum", "sanctumKind": "token", "sanctum_order": { "...": "..." }, "signers": [""] } ``` `sanctumKind` varies by route: | Strategy + kind | `sanctumKind` | | -------------------------- | --------------- | | `sol-liquid-stake instant` | `token` | | `sol-liquid-stake slow` | `withdrawStake` | | `sol-native-stake instant` | `depositStake` | Forward `hubra_token`, `sanctumKind`, and `sanctum_order` to `/broadcast`. ### Voltr-routed (`usdc-earn instant`) ```json theme={null} { "strategy": "usdc-earn", "kind": "instant", "transaction": "", "hubra_token": "", "route": "voltr", "signers": [""] } ``` Broadcast via `route: "rpc"`. ### Plain native deactivate (`sol-native-stake deactivate`) ```json theme={null} { "strategy": "sol-native-stake", "kind": "deactivate", "transaction": "", "hubra_token": "", "signers": [""], "notes": "Stake becomes withdrawable after the deactivation epoch (~2.5 days). Then call /api/v1/withdraw." } ``` No Sanctum or Voltr involvement. Broadcast via `route: "rpc"`. After the deactivation epoch passes, call [`POST /api/v1/withdraw`](/developer/endpoints/withdraw) to close the stake account and pull SOL back. ### Leveraged (`sol-leveraged-stake instant`) Burns raSOL Max LP (`amount`) and pays out **raSOL**. Routes through the rasol-max SDK's dual path — `path` tells you which ran: * `vault-idle` — one instruction, when the vault holds enough idle raSOL. * `flash-bracket` — 8–10 instructions + an address-lookup-table, deleveraging your slice through a MarginFi flashloan. The first redemption per wallet lazily initialises a MarginFi account (\~0.005 SOL one-shot rent), flagged by `initializedMarginfiAccount`. ```json theme={null} { "strategy": "sol-leveraged-stake", "kind": "instant", "outputAsset": "raSOL", "transaction": "", "hubra_token": "", "route": "rpc", "path": "flash-bracket", "burnedLpLamports": "19500000", "expectedPayoutRasol": "0.019485797", "expectedPayoutRasolLamports": "19485797", "initializedMarginfiAccount": false, "receiptMint": "HUBsveNpjo5pWqNkH57QzxjQASdTVXcSK7bVKTSZtcSX", "receiptSymbol": "raSOL", "signers": [""] } ``` Broadcast via `route: "rpc"`. #### Finishing to SOL (`outputAsset: "SOL"`) The leveraged exit pays out raSOL, not SOL. Pass `outputAsset: "SOL"` to get the **same single LP → raSOL transaction** plus a ready-to-run `next` step for the raSOL → SOL conversion: ```json theme={null} { "strategy": "sol-leveraged-stake", "outputAsset": "SOL", "transaction": "", "hubra_token": "", "path": "flash-bracket", "expectedPayoutRasol": "0.019414075", "next": { "description": "Broadcast THIS tx first, read the raSOL you actually received, then POST that amount here.", "endpoint": "/api/v1/unstake", "method": "POST", "body": { "strategy": "sol-liquid-stake", "wallet": "", "amount": "0.019414075", "kind": "instant" } } } ``` The SOL leg is a **second signed transaction**, not atomic. Sanctum won't quote raSOL → SOL until the burned raSOL actually lands in your wallet. So: broadcast the withdraw, read the raSOL your wallet actually received, then fire the `next` call sized by that real balance — `next.body.amount` is only the build-time estimate. *** ## Examples ```bash theme={null} # Native: epoch-bounded standard unstake curl -X POST https://hubra.app/api/v1/unstake \ -H 'Content-Type: application/json' \ -d '{"strategy":"sol-native-stake","wallet":"","stakeAccount":"","kind":"deactivate"}' # Native: instant settlement to SOL via Sanctum curl -X POST https://hubra.app/api/v1/unstake \ -H 'Content-Type: application/json' \ -d '{"strategy":"sol-native-stake","wallet":"","stakeAccount":"","amount":"1.0","kind":"instant"}' # Liquid: raSOL → SOL via Sanctum routing (instant) curl -X POST https://hubra.app/api/v1/unstake \ -H 'Content-Type: application/json' \ -d '{"strategy":"sol-liquid-stake","wallet":"","amount":"1.0","kind":"instant"}' # Liquid: raSOL → stake account, then epoch deactivation (slow) curl -X POST https://hubra.app/api/v1/unstake \ -H 'Content-Type: application/json' \ -d '{"strategy":"sol-liquid-stake","wallet":"","amount":"1.0","kind":"slow"}' # Leveraged: burn raSOL Max LP → raSOL curl -X POST https://hubra.app/api/v1/unstake \ -H 'Content-Type: application/json' \ -d '{"strategy":"sol-leveraged-stake","wallet":"","amount":"0.0195","kind":"instant"}' # Leveraged: burn raSOL Max LP, then chain to SOL (returns a `next` step) curl -X POST https://hubra.app/api/v1/unstake \ -H 'Content-Type: application/json' \ -d '{"strategy":"sol-leveraged-stake","wallet":"","amount":"0.0195","kind":"instant","outputAsset":"SOL"}' # USDC: full withdraw curl -X POST https://hubra.app/api/v1/unstake \ -H 'Content-Type: application/json' \ -d '{"strategy":"usdc-earn","wallet":"","kind":"instant","isWithdrawAll":true}' ``` *** ## Errors | Status | Slug | When | | ------ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `400` | `invalid_request` | Missing fields, invalid `kind` for the strategy, `stakeAccount` missing for `sol-native-stake`, missing `amount` for `sol-leveraged-stake`, or an invalid `outputAsset`. | | `404` | `not_found` | Unknown strategy key. | | `502` | `upstream_error` | Sanctum / Voltr could not build the transaction. | | `503` | `service_unavailable` | Strategy is announced but not live. | *** ## See also Preview the output before unstaking. Complete a native deactivate. Submit the signed transaction. # POST /withdraw Source: https://docs.hubra.app/developer/endpoints/withdraw Close a native stake account that has reached `inactive` state. Pulls lamports back to the wallet. ```http theme={null} POST https://hubra.app/api/v1/withdraw ``` Closes a native stake account that has reached `inactive` state. The companion to `/api/v1/unstake` for `sol-native-stake` deactivate flows: deactivate begins the cooldown, this completes the unstake once the deactivation epoch has passed. By default, withdraws all lamports back to the wallet (closing the account). Pass `lamports` for a partial drain. *** ## Lifecycle ``` 1. POST /api/v1/stake sol-native-stake → activating (epoch N) 2. POST /api/v1/unstake kind=deactivate → deactivating (epoch N+1...) 3. wait for deactivation epoch → inactive (~2 to 3 days later) 4. POST /api/v1/withdraw → close + refund SOL to wallet ``` The on-chain stake program enforces step 3. `StakeProgram.withdraw` rejects with `insufficient funds for instruction` if you call it before `currentEpoch > deactivationEpoch`. Do not poll faster than once per epoch. *** ## Request ```bash theme={null} curl -X POST https://hubra.app/api/v1/withdraw \ -H 'Content-Type: application/json' \ -d '{ "wallet": "", "stakeAccount": "" }' ``` | Field | Type | Required | Description | | -------------- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------- | | `wallet` | `string` | yes | Solana wallet pubkey (the withdraw authority). | | `stakeAccount` | `string` | yes | Pubkey of the stake account to close. Returned by your earlier `/stake` call. | | `lamports` | `number` | optional | Partial drain. If omitted, withdraws the account's full balance and closes the account. Must be a positive integer when provided. | *** ## Response ```json theme={null} { "strategy": "sol-native-stake", "transaction": "", "hubra_token": "", "stakeAccount": "", "withdrawnLamports": 1234567890, "signers": [""], "notes": "Withdraw closes the stake account if all lamports are pulled." } ``` The wallet (withdrawer authority) is the only required signer. Broadcast via `route: "rpc"` — there is no Sanctum or Voltr involvement. *** ## Errors | Status | Slug | When | | ------ | ----------------- | ---------------------------------------------------------------------- | | `400` | `invalid_request` | Missing `wallet` or `stakeAccount`, or non-positive `lamports`. | | `502` | `upstream_error` | Stake account does not exist on-chain, or RPC rejected the simulation. | ### Common upstream error: "insufficient funds for instruction" Returned at broadcast time when the stake account has not yet reached `inactive`. Wait until the on-chain epoch is strictly greater than the account's `deactivationEpoch` and try again. The Solana CLI's `solana stakes` "Inactive Stake: X SOL" line is misleading — it shows the *unlocked portion*, not the account state. The on-chain rule is what matters: an account is `inactive` when `currentEpoch > deactivationEpoch`. *** ## See also Begin deactivation (kind=deactivate). Submit the signed withdraw. # Errors Source: https://docs.hubra.app/developer/errors RFC 9457 problem-details, status codes, and the slug list. Errors follow [RFC 9457 problem-details](https://www.rfc-editor.org/rfc/rfc9457): ```json theme={null} { "type": "https://hubra.app/errors/", "title": "", "status": , "detail": "" } ``` Error responses use `Content-Type: application/problem+json`. Successful responses use plain `application/json`. *** ## Slugs and status codes | Slug | Status | When you see it | | --------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | | `invalid_request` | `400` | Body missing required fields, malformed JSON, unrecognized `kind`, bad `range` query, etc. | | `forbidden` | `403` | `/broadcast` rejected the request because `hubra_token` is missing, wrong, or expired. Rebuild via `/stake` or `/unstake` to get a fresh token. | | `not_found` | `404` | Unknown strategy key or unknown route. | | `method_not_allowed` | `405` | Hitting a `POST`-only endpoint with `GET`, etc. | | `upstream_error` | `502` | Sanctum / Voltr / Solana RPC returned an error or unexpected response. `detail` carries the upstream message when safe. | | `service_unavailable` | `503` | Strategy is announced but not live. Includes a `Retry-After` header. | | `internal_error` | `500` | Unhandled server error. Should be rare; log the response and retry. | *** ## Branch on `type`, not `title` The `type` slug is **stable and machine-readable**. The `title` is human-friendly and may evolve. Branch on `type` if you want to recover from specific failure modes: ```ts theme={null} async function broadcastWithRetry(body: object) { const res = await fetch("https://hubra.app/api/v1/broadcast", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); if (!res.ok) { const err = await res.json(); if (err.type?.endsWith("/forbidden")) { // Token expired or mismatched: rebuild. return rebuildAndRetry(); } if (err.type?.endsWith("/upstream_error")) { // Transient upstream issue: backoff + retry. return backoffAndRetry(); } throw new Error(err.detail ?? err.title); } return res.json(); } ``` *** ## Common patterns ### `502 upstream_error` from `/stake` for `sol-liquid-stake` Usually means Sanctum could not route a swap for your wallet. Common causes: * Wallet has no SOL on-chain (no associated account exists). * Wallet has no associated token account for raSOL yet. * Insufficient liquidity for the size you requested. Try a smaller amount or verify the wallet is funded. ### `502 upstream_error` from `/broadcast` Typically means the transaction failed simulation. Common causes: * Stale blockhash. Rebuild and re-sign. * Insufficient fee budget. * A missing signature slot. * The signing path produced different message bytes than the build path. ### `403 forbidden` from `/broadcast` `hubra_token` mismatch. See [Hubra token](/developer/hubra-token) for the full list of causes. Fix is always: rebuild via `/stake` or `/unstake`, sign that new transaction, broadcast with the new token. ### `503 service_unavailable` on a strategy The strategy is announced in the manifest but not yet live. The response includes a `Retry-After` header (typically `3600` seconds). Plan around it; do not retry hot. *** ## Retry policy Recommended approach by slug: | Slug | Retry? | How | | --------------------- | ------------------------ | ---------------------------------------------------- | | `invalid_request` | No | Fix the request body. | | `forbidden` | Yes, after rebuild | Rebuild via `/stake` or `/unstake`, sign, broadcast. | | `not_found` | No | Fix the strategy key or route. | | `method_not_allowed` | No | Fix the HTTP method. | | `upstream_error` | Yes, with backoff | Exponential backoff. 3 retries max. | | `service_unavailable` | Yes, after `Retry-After` | Honor the header. | | `internal_error` | Yes, with backoff | Exponential backoff. Report if persistent. | *** ## Reporting bugs If you hit `internal_error` repeatedly, or an `upstream_error` with a `detail` that does not match any documented upstream behavior, file an issue at [github.com/block-sync-one/hubra](https://github.com/block-sync-one/hubra) or email [hello@hubra.app](mailto:hello@hubra.app). Include: * The full request body. * The full response body (including `type`, `title`, `status`, `detail`). * The `X-Hubra-Api-Version` and any `commit` from `/health`. * The approximate time of the request. # Glossary Source: https://docs.hubra.app/developer/glossary Solana primitives, staking flows, and Hubra-specific terms. A reference for engineers and agents not deeply familiar with Solana's stake program, LSTs, or vault primitives. For positioning and audience context, see the marketing pages instead. *** ## Solana primitives ### Lamport Smallest unit of SOL. `1 SOL = 1,000,000,000 lamports` (10⁹). Base unit for on-chain math; user-facing amounts are decimal SOL. ### Pubkey Base58-encoded ed25519 public key, 32–44 chars. Identifies wallets, mints, programs, accounts. ### Mint Pubkey of the SPL token mint account. `So11111111111111111111111111111111111111112` is wrapped SOL. ### Stake account A specific Solana program account holding delegated stake. Created fresh each time via `StakeProgram.createAccount`. Has its own keypair (one-time signer at creation), a staker authority, and a withdrawer authority. Hubra sets both authorities to the user's wallet. ### Vote account A Solana program account a validator publishes votes to. Hubra's vote account is `7K8DVxtNJGnMtUY1CQJT5jcs8sFGSZTDiG7kowvFpECh`. Stake accounts delegate to a vote account. ### Epoch Solana's unit of time, \~2 to 3 days. Stake activation, deactivation, and reward settlement are bounded by epoch transitions. ### Slot A leader-assigned time interval on Solana, \~400ms target. An epoch is approximately 432,000 slots. *** ## Staking flows ### Native delegation User creates a stake account, funds it from their wallet, delegates it to a vote account. Self-custodial: the wallet retains both staker and withdrawer authority. Rewards mint into the stake account each epoch. ### Liquid staking User deposits SOL into a stake pool and receives a transferable receipt token (an LST). Hubra's LST is **raSOL** (`HUBsveNpjo5pWqNkH57QzxjQASdTVXcSK7bVKTSZtcSX`), minted via Sanctum. ### Receipt token / LST Liquid Staking Token. raSOL is non-rebasing: the conversion rate to SOL grows each epoch as underlying stake earns rewards. The token itself stays at a fixed supply per deposit; appreciation lives in the rate, not the balance. ### Exchange rate SOL per receipt. raSOL trades > 1 SOL/raSOL once it has accrued rewards. *** ## Unstake mechanics ### Deactivation `StakeProgram.deactivate`. Begins the epoch-bounded cooldown on a stake account. Account becomes withdrawable after the deactivation epoch passes (typically 1 epoch, worst case 2). ### Slow unstake (LST) Convert raSOL → native stake account via Sanctum's `withdrawStake`. Then deactivate normally. Combined \~2 to 3 days settlement. ### Instant unstake (LST) Swap raSOL → SOL via routed liquidity (Sanctum's swap router). Pays a small price-impact fee but settles in a block. ### Instant unstake (native) Sanctum's `depositStake` consumes an active stake account and returns SOL immediately. Pays a price-impact fee. ### Partial unstake Splitting an existing stake account on-chain before routing the slice you specified. Costs \~0.002 SOL of rent (covered by Hubra) for the new account. *** ## USDC vault terms ### raUSDC Hubra Earn vault receipt mint (`53fZaJGDMHcfku8pzZak5obVFUUjVxwqRTF63M3SQiSS`). Non-rebasing; appreciates against USDC. ### Voltr vault `3maCuTJVPteZ2dFA8dADxz2EbpJHfoAG5txYhXDs6gNQ`. The vault routes USDC across Kamino + Jupiter for blended yield. Withdrawals are instant. ### Adapter A whitelisted contract integration that the Voltr vault is allowed to deposit into. Each adapter is independently audited. *** ## Hubra API terms ### Strategy key Canonical identifier for a Hubra route: `sol-native-stake`, `sol-liquid-stake`, `sol-leveraged-stake`, `usdc-earn`. Used as a path or body parameter on every endpoint that addresses a specific strategy. ### Hubra token HMAC over an unsigned transaction's message bytes. Returned by `/stake` / `/unstake` / `/withdraw`, required by `/broadcast`. See [Hubra token](/developer/hubra-token). ### Sanctum order The full order response Sanctum returned when building a swap-routed transaction. Hubra forwards this in `sanctum_order` so the agent can pass it to `/broadcast` for Sanctum's execute endpoint to validate. ### Sanctum kind The category of Sanctum operation: `token` (raSOL token swap), `depositStake` (active stake → SOL), `depositSol` (SOL → LST), `withdrawStake` (raSOL → native stake account). Required at broadcast time when `route="sanctum"`. *** ## See also * [raSOL](/general/raSOL) and [raUSDC](/general/raUSDC) for token references. * [Solana docs](https://solana.com/docs/economics) for canonical protocol documentation. # Hubra token Source: https://docs.hubra.app/developer/hubra-token The HMAC token that gates /broadcast. Why it exists, how it works, what to do with it. `POST /api/v1/broadcast` requires a `hubra_token` on every call. This page explains why it exists and how to use it. *** ## What it is A **HMAC token** issued by `/stake`, `/unstake`, and `/withdraw`. The token is computed over the message bytes of the unsigned transaction the endpoint just built. ``` hubra_token = HMAC(server_secret, message_bytes_of_unsigned_tx) ``` The token is opaque to the agent. Treat it as a string. Pass it back to `/broadcast` alongside the signed transaction. *** ## Why it exists Without a gate, `/broadcast` would be a free Solana RPC for arbitrary signed transactions. The server has cost (RPC quota, MEV protection budget, Sanctum execute slots) on every broadcast; allowing arbitrary transactions opens a denial-of-service vector. The HMAC token binds a specific broadcast to a specific Hubra-built transaction: * `/broadcast` recomputes the HMAC over the signed transaction's message bytes. * If it matches, the broadcast proceeds. * If it does not, `/broadcast` returns `403 forbidden`. This keeps the broadcast endpoint useful only for transactions that Hubra actually built, while staying transparent: the agent does not need to authenticate, register, or carry an API key. *** ## Lifetime Tokens are valid for **\~2 minutes** after issue. This matches Solana's blockhash window: a transaction with an expired blockhash cannot be broadcast successfully anyway, so a longer-lived token would not help. If your token expires, rebuild via `/stake`, `/unstake`, or `/withdraw` to get a fresh one. The build is cheap. *** ## Sanctum-specific fields For Sanctum-routed flows, the broadcast also needs `sanctumKind` and `sanctum_order`. These are returned alongside `hubra_token` in the build response: ```json theme={null} { "transaction": "", "hubra_token": "", "route": "sanctum", "sanctumKind": "token", "sanctum_order": { "...": "..." }, ... } ``` Forward all four fields (`hubra_token`, `route`, `sanctumKind`, `sanctum_order`) to `/broadcast`. Sanctum's execute endpoint independently validates the signed transaction against the original `sanctum_order`; without it, Sanctum rejects the broadcast. For non-Sanctum flows, only `hubra_token` is needed. *** ## What to save When you call `/stake` or `/unstake`, save: | | Always | Sanctum-routed only | | -------- | ---------------------------- | ------------------------------ | | Required | `transaction`, `hubra_token` | `sanctumKind`, `sanctum_order` | For `sol-native-stake`, also save `stakeAccount` (the new pubkey) — you need it for future deactivate / withdraw / instant unstake. *** ## What you do not need You do not need to: * Decode or parse the token. * Refresh it manually (just rebuild if expired). * Sign or transform it in any way. * Persist it beyond the broadcast. Treat the token as a single-use, short-lived bearer credential for one specific transaction. *** ## Error: token mismatch If `/broadcast` returns `403 forbidden`: ```json theme={null} { "type": "https://hubra.app/errors/forbidden", "title": "Forbidden", "status": 403, "detail": "hubra_token does not match this transaction or has expired." } ``` Common causes: * The token has expired (>2 minutes since issue). Rebuild. * The signed transaction's message bytes do not match the unsigned bytes (you re-serialized differently). Make sure `tx.serialize()` after signing produces the same message slice as the original. * The token belongs to a different transaction (you mixed up two parallel build responses). In all three cases, the fix is to rebuild the transaction via `/stake` or `/unstake`, sign that new transaction, and broadcast with the new token. *** ## See also The endpoint that uses the token. `403 forbidden` and other error slugs. # Developer overview Source: https://docs.hubra.app/developer/overview The Hubra Agent API: an HTTP surface that mirrors the human staking app, designed to be callable by AI agents. The Hubra Agent API is an HTTP surface that does what the Hubra app does, by JSON. **Partners** — building an integration that brings liquidity to Hubra? Get a referral code at [partner.hubra.app](https://partner.hubra.app) and pass it on your stake calls to attribute the liquidity you bring in and track it on your dashboard. See [Referral attribution](/developer/endpoints/stake#referral-attribution). Reads are public. Writes return **unsigned Solana transactions** that the caller signs locally with their own keypair, then submits via `/broadcast`. Hubra never holds a key. No API key. No sign-up. No database. The on-chain signature on the actual transaction is what authorizes the user's intent. *** ## What it is A thin REST layer over the same server actions that power the human Hubra app. There is no second source of truth: when an agent stakes 1 SOL via `POST /api/v1/stake`, the underlying mechanism is identical to a human clicking "Stake" in the UI. ``` ┌──────────────── HUMAN SURFACE ────────────────┐ │ hubra.app/s — wallet connect, click to stake │ └───────────────────────────────────────────────┘ │ │ shared server actions ▼ ┌──────────────── AGENT SURFACE ────────────────┐ │ api/v1/strategies list strategies │ │ api/v1/quote preview unstake │ │ api/v1/stake build unsigned tx │ │ api/v1/unstake build unsigned tx │ │ api/v1/withdraw close native stake │ │ api/v1/broadcast submit signed tx │ └───────────────────────────────────────────────┘ ``` *** ## Auth model There is no API key. There is no sign-up. Every endpoint accepts requests from any caller. Authorization comes from the on-chain signature on the actual stake transaction. If the wallet that signed the transaction does not own the assets being staked, the chain rejects the transaction. The HTTP layer adds nothing beyond that. If you want to attribute requests to a wallet for future loyalty/points, send `X-Hubra-Wallet: ` as a header. It is optional and currently informational only. *** ## Base URL and versioning ``` https://hubra.app/api/v1 ``` All endpoints under `/api/v1`. Breaking changes ship as `/api/v2`. Every response includes the header: ``` X-Hubra-Api-Version: v1 ``` *** ## Conventions | | | | ------------ | ------------------------------------------------------------------------------- | | Content type | `application/json` on requests and successful responses | | Error type | `application/problem+json` ([RFC 9457](https://www.rfc-editor.org/rfc/rfc9457)) | | Amounts | Decimal strings (`"1.5"`), never floats | | CORS | Permissive `Access-Control-Allow-Origin: *` on read endpoints | For the full conventions reference, see [Conventions](/developer/conventions). *** ## Strategy keys The four canonical paths: | Key | Status | Asset | Description | | --------------------- | ------ | ----- | ---------------------------------------- | | `sol-native-stake` | live | SOL | Direct delegation to Hubra's validator | | `sol-liquid-stake` | live | SOL | Mint raSOL via Sanctum | | `sol-leveraged-stake` | live | SOL | Auto-managed leveraged raSOL (raSOL Max) | | `usdc-earn` | live | USDC | Voltr-routed USDC vault | For the full strategy reference (intros, steps, on-chain handles), see [Strategies](/developer/strategies) or call [`GET /api/v1/strategies`](/developer/endpoints/list-strategies). *** ## Postman collection Import the full Agent API into Postman to try every endpoint without writing code. `hubra-agent-api.postman_collection.json` — all read and write endpoints, pre-configured. *** ## End-to-end flow ```bash theme={null} # 1. Discover curl https://hubra.app/api/v1/strategies # 2. (Optional) Preview an unstake curl -X POST https://hubra.app/api/v1/quote \ -H 'Content-Type: application/json' \ -d '{"strategy":"sol-liquid-stake","wallet":"","amount":"1.5"}' # 3. Build an unsigned stake tx curl -X POST https://hubra.app/api/v1/stake \ -H 'Content-Type: application/json' \ -d '{"strategy":"sol-liquid-stake","wallet":"","amount":"1.5"}' # → { "transaction": "", "hubra_token": "...", ... } # 4. Sign the tx locally with your Solana keypair # 5. Broadcast curl -X POST https://hubra.app/api/v1/broadcast \ -H 'Content-Type: application/json' \ -d '{"signed_tx":"","hubra_token":""}' # → { "signature": "5z…", "explorer": "https://solscan.io/tx/5z…" } ``` *** ## The `hubra_token` gate `/stake` and `/unstake` responses include a `hubra_token` (an HMAC over the unsigned transaction's message bytes). `/broadcast` requires this token; without a matching token, it rejects the request. This prevents `/broadcast` from being used as a free Solana RPC for arbitrary transactions. Tokens are valid for \~2 minutes (matching Solana's blockhash window). Rebuild via `/stake` or `/unstake` if expired. For Sanctum-routed flows, you also need to forward `sanctumKind` and `sanctum_order` from the build response to `/broadcast`. See [Hubra token](/developer/hubra-token) for the full mechanics. *** ## What's next First request to first stake transaction. JSON, errors, decimals, CORS. The strategy registry. `POST /api/v1/stake` reference. # Quickstart Source: https://docs.hubra.app/developer/quickstart Mint raSOL by HTTP in under five minutes. This walks through staking 1 SOL into raSOL using only `curl` and a Solana keypair. The same shape applies to native staking and USDC Earn. You need a funded Solana wallet (at least the amount you want to stake plus a few thousand lamports for safety; Hubra covers gas, but you need the asset to stake itself). And a way to sign transactions locally. *** ## 1. Discover the strategies ```bash theme={null} curl https://hubra.app/api/v1/strategies ``` Response: ```json theme={null} { "strategies": [ { "key": "sol-native-stake", "asset": "SOL", "title": "Native", "blurb": "Delegate SOL to Hubra's validator.", "status": "live", "live": { "apy": 6.7, "exchangeRate": null } }, { "key": "sol-liquid-stake", "asset": "SOL", "title": "Liquid", "blurb": "Mint raSOL via Sanctum.", "status": "live", "live": { "apy": 6.4, "exchangeRate": 1.0723 } }, { "key": "sol-leveraged-stake", "asset": "SOL", "title": "Leveraged raSOL Max", "blurb": "Auto-managed leveraged raSOL. Amplified staking yield, no active management.", "status": "live", "live": { "apy": 9.93, "exchangeRate": 1.1988 } }, { "key": "usdc-earn", "asset": "USDC", "title": "Earn", "blurb": "Routed USDC vault.", "status": "live", "live": { "apy": 5.6, "exchangeRate": 1.012 } } ] } ``` Pick `sol-liquid-stake`. (All four keys are live and agent-callable — including `sol-leveraged-stake`, which deposits SOL into raSOL Max. See [Strategies → `sol-leveraged-stake`](/developer/strategies#sol-leveraged-stake).) *** ## 2. Build the unsigned transaction ```bash theme={null} curl -X POST https://hubra.app/api/v1/stake \ -H 'Content-Type: application/json' \ -d '{ "strategy": "sol-liquid-stake", "wallet": "", "amount": "1.0" }' ``` Response: ```json theme={null} { "strategy": "sol-liquid-stake", "transaction": "", "hubra_token": "", "route": "sanctum", "sanctumKind": "token", "sanctum_order": { "...": "..." }, "signers": [""] } ``` Save `transaction`, `hubra_token`, `sanctumKind`, and `sanctum_order`. You will pass all four back to `/broadcast`. *** ## 3. Sign the transaction Using `@solana/web3.js`: ```ts theme={null} import { VersionedTransaction, Keypair } from "@solana/web3.js"; const tx = VersionedTransaction.deserialize( Buffer.from(buildResponse.transaction, "base64"), ); tx.sign([wallet]); // wallet is a Keypair you hold const signed = Buffer.from(tx.serialize()).toString("base64"); ``` For native staking, `tx.sign([wallet])` only fills your wallet's slot. The stake-account slot is pre-signed by Hubra's server. *** ## 4. Broadcast For Sanctum-routed flows (any liquid stake or instant native unstake), forward the Sanctum-specific fields: ```bash theme={null} curl -X POST https://hubra.app/api/v1/broadcast \ -H 'Content-Type: application/json' \ -d '{ "signed_tx": "", "hubra_token": "", "route": "sanctum", "sanctumKind": "token", "sanctum_order": { "...": "..." } }' ``` For non-Sanctum flows (native delegation, native withdraw, USDC Earn), plain RPC is fine: ```bash theme={null} curl -X POST https://hubra.app/api/v1/broadcast \ -H 'Content-Type: application/json' \ -d '{ "signed_tx": "", "hubra_token": "" }' ``` Response: ```json theme={null} { "signature": "5z6Z...", "explorer": "https://solscan.io/tx/5z6Z..." } ``` You now hold raSOL. *** ## Adapting to other strategies The shape is identical for all four agent-callable strategies. What changes: * **Native stake (`sol-native-stake`):** the response includes a fresh `stakeAccount` pubkey. Save it; you need it to deactivate or instant-unstake later. Broadcast via `route: "rpc"` for stake, `route: "sanctum"` for instant unstake. * **Liquid stake (`sol-liquid-stake`):** Sanctum-routed always. * **USDC Earn (`usdc-earn`):** broadcast via `route: "rpc"`. No `sanctum_order` to forward. * **Leveraged raSOL Max (`sol-leveraged-stake`):** deposit `amount` in SOL — one tx does SOL → raSOL → raSOL Max LP. Unstake burns raSOL Max LP → raSOL (`outputAsset: "SOL"` adds a chained step to finish to SOL). Broadcast via `route: "rpc"`. See the per-endpoint references for the response shapes: `POST /api/v1/stake` `POST /api/v1/unstake` `POST /api/v1/withdraw` `POST /api/v1/broadcast` # Strategies Source: https://docs.hubra.app/developer/strategies The canonical strategy registry. Four keys, four asset routes. The Hubra Agent API exposes four canonical strategies. Adding a new strategy is one entry in the server-side registry; the agent surface picks it up automatically. *** ## Strategy keys | Key | Asset | Route | Status | | --------------------- | ----- | ---------------------------------------- | ------ | | `sol-native-stake` | SOL | Native delegation to Hubra's validator | live | | `sol-liquid-stake` | SOL | Mint raSOL via Sanctum | live | | `sol-leveraged-stake` | SOL | Auto-managed leveraged raSOL (raSOL Max) | live | | `usdc-earn` | USDC | Voltr USDC vault | live | All four are returned by [`GET /api/v1/strategies`](/developer/endpoints/list-strategies). Each has a detailed view at [`GET /api/v1/strategies/{key}`](/developer/endpoints/get-strategy). All four are fully agent-callable: `actions.stake` and `actions.unstake` are populated for each, and `/api/v1/stake`, `/api/v1/unstake`, and `/api/v1/quote` build transactions for them. *** ## `sol-native-stake` Direct delegation to Hubra's Solana validator. The agent's wallet creates a stake account, delegates voting rights to Hubra's vote account, and retains both stake authority and withdraw authority. | | | | ---------------------- | ---------------------------------------------- | | Asset in | SOL | | Asset out | None (you keep the stake account) | | Validator vote account | `7K8DVxtNJGnMtUY1CQJT5jcs8sFGSZTDiG7kowvFpECh` | | Stake actions | `delegate` | | Unstake actions | `deactivate`, `instant` | ### Stake mechanics `POST /api/v1/stake` returns a partially-signed transaction. The server generates a fresh stake-account keypair and pre-signs that signature slot. The agent only signs as the wallet. The new stake-account pubkey is returned in the response under `stakeAccount` — **save it**, you need it for future deactivate / withdraw / instant unstake. ### Unstake mechanics * `kind: "deactivate"` triggers `StakeProgram.deactivate`. After the deactivation epoch (\~2 to 3 days), call [`POST /api/v1/withdraw`](/developer/endpoints/withdraw) to close the stake account and pull SOL back. * `kind: "instant"` routes the active stake account through Sanctum's `depositStake` for immediate SOL. Both kinds require the `stakeAccount` pubkey returned by the original `/stake` call. *** ## `sol-liquid-stake` Mint raSOL by depositing SOL into Sanctum. raSOL is a non-rebasing receipt token for SOL staked with Hubra's validator. | | | | --------------- | ------------------------------------------------------ | | Asset in | SOL | | Asset out | raSOL (`HUBsveNpjo5pWqNkH57QzxjQASdTVXcSK7bVKTSZtcSX`) | | Stake actions | `mint` | | Unstake actions | `instant`, `slow` | ### Stake mechanics `POST /api/v1/stake` returns an unsigned Sanctum-routed transaction. The response carries `route: "sanctum"`, `sanctumKind: "token"`, and a `sanctum_order` object. **Forward all three (plus `hubra_token`) to `/broadcast`** if you want Sanctum's MEV-protected broadcaster. ### Unstake mechanics * `kind: "instant"` swaps raSOL → SOL via Sanctum's pooled LST liquidity. * `kind: "slow"` runs `Sanctum withdrawStake` to convert raSOL into a native stake account, then standard epoch deactivation. Both pass through Sanctum and require the Sanctum-specific fields at broadcast time. *** ## `sol-leveraged-stake` Hubra's auto-managed leveraged raSOL strategy (raSOL Max). Deposit SOL, receive a raSOL Max LP receipt whose redemption rate drifts up each epoch as the leveraged position earns amplified staking yield. The strategy levers up when conditions are favorable and unwinds when borrow costs rise; you don't manage anything. | | | | ------------------ | ------------------------------------------------------------- | | Asset in (API) | SOL (`So11111111111111111111111111111111111111112`) | | Receipt | raSOL Max LP (`CJEYakpjmKBvvUzAn3HJSs9vtijnv472T8YJEV3WzToF`) | | Vault deposit mint | raSOL (`HUBsveNpjo5pWqNkH57QzxjQASdTVXcSK7bVKTSZtcSX`) | | Stake actions | `deposit` | | Unstake actions | `instant` | ### Live data `GET /api/v1/strategies/sol-leveraged-stake` returns the live block: * `live.apy` — latest captured leveraged APY in percent, from the daily-cron `loadRasolMaxApySnapshot` source the leverage page also reads. * `live.exchangeRate` — NAV-per-LP (1 raSOL Max → N raSOL), the redemption rate at the latest captured epoch. `GET /api/v1/apy/history?strategy=sol-leveraged-stake` returns the per-epoch bar-chart series (\~12 points). Since the upstream is a single snapshot stream, the same series is returned under each range key. ### Stake mechanics `POST /api/v1/stake` takes `amount` in **SOL** and returns a single unsigned transaction that does the whole conversion: create the raSOL ATA (idempotent) → SPL stake-pool `DepositSol` (SOL → raSOL) → Voltr `depositVault` (raSOL → raSOL Max LP). One signature, atomic. Broadcast via `route: "rpc"`. The vault ingests raSOL internally, but the agent endpoint deposits SOL only — there is no "deposit raSOL directly" variant. ### Unstake mechanics `POST /api/v1/unstake` with `kind: "instant"` burns raSOL Max LP (`amount` = LP to burn) and pays out **raSOL**, via the rasol-max SDK's `vault-idle` (1 ix) or `flash-bracket` (8–10 ixs + ALT) path — the response `path` field tells you which. The first redemption per wallet lazily initialises a MarginFi account (\~0.005 SOL one-shot rent). Broadcast via `route: "rpc"`. The leveraged exit stops at raSOL. To finish to SOL, pass `outputAsset: "SOL"` — the response keeps the same single LP → raSOL transaction and adds a `next` step that converts the resulting raSOL → SOL via a `sol-liquid-stake` instant unstake (a separate, non-atomic second tx). See [`POST /api/v1/unstake`](/developer/endpoints/unstake#finishing-to-sol-outputasset-sol). ### Quote `POST /api/v1/quote` previews the raSOL payout for burning a given LP amount and the redemption `path`. There is no deposit-side quote — the SOL → raSOL mint is a deterministic floor-division from the live pool snapshot. *** ## `usdc-earn` Deposit USDC into the Voltr-routed Hubra Earn vault. | | | | --------------- | ------------------------------------------------------- | | Asset in | USDC | | Asset out | raUSDC (`53fZaJGDMHcfku8pzZak5obVFUUjVxwqRTF63M3SQiSS`) | | Vault | `3maCuTJVPteZ2dFA8dADxz2EbpJHfoAG5txYhXDs6gNQ` | | Stake actions | `deposit` | | Unstake actions | `instant` | ### Stake mechanics `POST /api/v1/stake` returns an unsigned Voltr deposit transaction. Broadcast via `route: "rpc"`. There is no `sanctum_order` to forward. ### Unstake mechanics * `kind: "instant"` runs a Voltr direct withdraw. No cooldown, no fee. * Pass `isWithdrawAll: true` to drain the position fully without specifying an amount. Both broadcast via plain RPC. *** ## Live data Every strategy entry includes a `live` block with the latest APY and (where applicable) the receipt-rate-to-asset exchange rate. ```json theme={null} { "key": "sol-liquid-stake", "asset": "SOL", "title": "Liquid", "blurb": "Mint raSOL via Sanctum.", "status": "live", "live": { "apy": 6.4, "exchangeRate": 1.0723 } } ``` `apy` is a percentage (`6.4` = 6.4%). `exchangeRate` is the current asset-per-receipt rate; null where it does not apply (native staking does not have a receipt token). For time-series APY, see [`GET /api/v1/apy/history`](/developer/endpoints/apy-history). *** ## Coming soon strategies The strategy registry can flag a key as `coming_soon` (announced but not live). When that flag is set: * `GET /api/v1/strategies` returns the entry with `status: "coming_soon"`. * `GET /api/v1/strategies/{key}` returns `503 service_unavailable` with a `Retry-After` hint. * Stake / unstake / quote calls against the key return `503` until launch. This keeps the manifest stable: agents can plan around an upcoming strategy without it being a hard failure surface. There are no `coming_soon` strategies in the registry today — all four keys are `live` and fully agent-callable. *** ## What's next List all strategies. Per-strategy detail. Build an unsigned stake tx. Build an unsigned unstake tx. # GitHub Source: https://docs.hubra.app/general/github Hubra's open-source repositories. Hubra publishes the parts of its infrastructure that are useful to read. The validator, the vault routing, and the agent surface are all reviewable. *** ## Organization [github.com/block-sync-one](https://github.com/block-sync-one) hosts the active codebases. *** ## Notable repositories The main Hubra application: marketing surface, app, and agent API. The autonomous rebalancer that allocates USDC across whitelisted venues. *** ## Why open source matters here The vault rebalancer is the piece of Hubra that decides where USDC moves. Reading the code is the most direct way to verify what the rebalancer can and cannot do, what protocols it has access to, and how it makes allocation decisions. The validator's vote account is on-chain and verifiable on Solscan; that is its open record. The agent API surface is documented in the [Developer](/developer/overview) tab of these docs. # Brand kit Source: https://docs.hubra.app/general/press-kit Hubra logos, colors, fonts, and design assets for partners and press. A brand kit is a set of visual and textual elements that establish a brand's identity. Use the assets below for any design or partnership material. The brand kit lives in this documentation repo and is versioned in public — it is the source of truth. Link to it on GitHub; do not copy assets into shared drives. *** ## Download All logos, marks, and assets — versioned in the docs repo. Clear space, color, and do-not rules in one place. *** ## Logos | Asset | Use | File | | ---------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Wordmark — dark | Full logo, white, for dark backgrounds | [`hubra-wordmark-dark.svg`](https://raw.githubusercontent.com/Hubra-labs/docs/main/brandkit/logo/hubra-wordmark-dark.svg) | | Wordmark — light | Full logo, black, for light backgrounds | [`hubra-wordmark-light.svg`](https://raw.githubusercontent.com/Hubra-labs/docs/main/brandkit/logo/hubra-wordmark-light.svg) | | Mark | The mark alone — favicons, avatars, app tiles | [`hubra-mark.svg`](https://raw.githubusercontent.com/Hubra-labs/docs/main/brandkit/logo/hubra-mark.svg) · [`.png`](https://raw.githubusercontent.com/Hubra-labs/docs/main/brandkit/logo/hubra-mark.png) | | App icon | The mark on the gold icon tile | [`hubra-icon.svg`](https://raw.githubusercontent.com/Hubra-labs/docs/main/brandkit/logo/hubra-icon.svg) · [`.png`](https://raw.githubusercontent.com/Hubra-labs/docs/main/brandkit/logo/hubra-icon.png) | ### Social | Asset | Use | File | | --------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Banner | 1200×600 — social headers, partner decks | [`hubra-banner.svg`](https://raw.githubusercontent.com/Hubra-labs/docs/main/brandkit/social/hubra-banner.svg) | | Open Graph card | 1200×500 — link previews | [`hubra-og.svg`](https://raw.githubusercontent.com/Hubra-labs/docs/main/brandkit/social/hubra-og.svg) · [`.png`](https://raw.githubusercontent.com/Hubra-labs/docs/main/brandkit/social/hubra-og.png) | ### Token marks | Asset | File | | ------ | ------------------------------------------------------------------------------------------------ | | raSOL | [`raSOL.svg`](https://raw.githubusercontent.com/Hubra-labs/docs/main/brandkit/token/raSOL.svg) | | raUSDC | [`raUSDC.svg`](https://raw.githubusercontent.com/Hubra-labs/docs/main/brandkit/token/raUSDC.svg) | ### Using the logo * Keep clear space around the logo equal to the height of the mark. * Do not recolor, rotate, stretch, or add effects to the logo or mark. * Do not place the wordmark on a busy background — use a solid Night or Sky surface. * Use the dark wordmark on light, the light wordmark on dark. Never invert manually. *** ## At a glance | | | | ------- | -------------------------------------------- | | Tagline | Your stake, trusted. | | Founded | 2020 (continuous Solana validator operation) | | Tone | Established. Restrained. Considered. | *** ## Colors | Role | Name | Hex | | ----------------------- | --------------- | --------- | | Signature | Sun Gold | `#DAA520` | | Signature — bright tint | Sun Gold Bright | `#F5C84B` | | Accent | Falcon Blue | `#38ACF9` | | Surface (dark) | Night | `#0A0A0A` | | Surface (light) | Sky | `#FFFFFF` | Gold is signature ink, used sparingly. Hubra's marketing surfaces are warm dark or quiet light; gold is rare punctuation, never a fill color. *** ## Typography | Role | Family | | ------------------- | ------------- | | UI / body | Manrope | | Display / headlines | Space Grotesk | *** ## Voice A short list of what to do and what to avoid when writing about Hubra: **Do** * State numbers as facts. "Six years of continuous operation." Not "the most trusted validator." * Use "delegate" for native staking; not "send." * Call validators by their public vote identity when verification matters. * Treat the user as an adult making a measured decision. **Don't** * Use "premium banking" or other generic financial-marketing copy. * Call rewards "guaranteed." * Use rocket emojis or countdowns. * Write "lamports" in user-facing copy. Use SOL throughout. *** ## Partnerships For partnership designs, co-marketing, or other custom collaborations, reach out: [hello@hubra.app](mailto:hello@hubra.app) # raSOL Source: https://docs.hubra.app/general/raSOL Token reference for Hubra's liquid staking token. raSOL is Hubra's liquid staking token. This page is the **token reference**: addresses, mechanics, infrastructure. For the user-facing product story, see [Liquid staking](/overview/liquid-staking). *** ## Token data | | | | ----------------- | ----------------------------------------------------------- | | Name | Hubra staked SOL | | Symbol | raSOL | | Standard | SPL Token | | Mint address | `HUBsveNpjo5pWqNkH57QzxjQASdTVXcSK7bVKTSZtcSX` | | Decimals | 9 | | Infrastructure | Sanctum Infinity | | Backing validator | Hubra (vote `7K8DVxtNJGnMtUY1CQJT5jcs8sFGSZTDiG7kowvFpECh`) | [View on Solscan](https://solscan.io/token/HUBsveNpjo5pWqNkH57QzxjQASdTVXcSK7bVKTSZtcSX). *** ## Token model raSOL is a **non-rebasing, value-accruing** receipt token. * **Non-rebasing:** the supply per deposit is fixed at mint. Your raSOL balance does not grow over time. * **Value-accruing:** the redemption rate (SOL per raSOL) climbs each epoch as the underlying stake earns rewards. ```text theme={null} raSOL_balance × current_rate = your_underlying_SOL ``` Yield is delivered through the rate, not the balance. This is the same model Aave's aTokens, Lido's wstETH, and Sanctum's preferred-partner LSTs use. *** ## Where the yield comes from Every raSOL is backed by SOL staked to the **Hubra validator**. The validator earns Solana's standard staking rewards at every epoch boundary (\~2 to 3 days). Rewards flow into the raSOL exchange rate. There is no second yield layer. raSOL's APY is exactly the underlying validator's net APY (issuance × (1 − commission)). Live APY: [`GET /api/v1/strategies/sol-liquid-stake`](/developer/endpoints/get-strategy). *** ## Where you can use raSOL raSOL is a Sanctum preferred-partner LST. That status comes with deep pooled liquidity across Solana DeFi: * **Lending and collateral:** Kamino, Save, Loopscale. * **DEX swaps:** Jupiter, Orca, Meteora, Raydium, Titan. * **LST-to-LST conversions:** Sanctum router (raSOL ↔ JitoSOL, mSOL, INF, and the rest of the Sanctum LST set). Because raSOL is a standard SPL token, anything that supports SPL tokens supports raSOL by default. *** ## Redemption paths Three ways to redeem raSOL for SOL: | Path | Time | Fee | Mechanics | | -------------------------------------- | ------------- | ------------------------- | ------------------------------------------------- | | Sell on a DEX | Instant | DEX fee + price impact | Standard token swap | | Sanctum instant unstake | One block | Sanctum price impact only | Pooled LST liquidity swap | | Sanctum slow unstake (`withdrawStake`) | \~2 to 3 days | None | raSOL → native stake account → epoch deactivation | The instant and slow paths run inside the Hubra app; sell-on-DEX you can do anywhere SPL tokens trade. *** ## Risks raSOL inherits all native validator risks plus a smart-contract layer. * **Smart-contract risk.** Sanctum infrastructure is heavily used and audited but not invulnerable. * **Validator risk.** raSOL's yield depends on Hubra's validator performance. * **Temporary depeg.** In stressed markets, raSOL might trade below its fair SOL value on DEXs. Instant unstake redeems at the true exchange rate even when secondary markets wobble. * **Liquidity risk.** Very large unstakes may incur material price impact. Quote first. *** ## Working with raSOL programmatically For agents and developers: * Get the live exchange rate: [`GET /api/v1/strategies/sol-liquid-stake`](/developer/endpoints/get-strategy). * Build a stake transaction (SOL → raSOL): [`POST /api/v1/stake`](/developer/endpoints/stake) with `strategy: "sol-liquid-stake"`. * Build an unstake transaction (raSOL → SOL): [`POST /api/v1/unstake`](/developer/endpoints/unstake) with `strategy: "sol-liquid-stake"` and `kind: "instant"` or `"slow"`. *** ## Common questions raSOL is non-rebasing. Yield lives in the redemption rate, not the balance. Multiply your balance by the current rate (visible in the app or via the API) to see your SOL value. raSOL is a Solana-native SPL token. Bridge support depends on third-party bridges; we do not maintain bridges directly. Same primitive, different validator backing. raSOL stakes to Hubra's validator only. JitoSOL and mSOL stake across many validators per their delegation strategies. raSOL also runs entirely on Sanctum's preferred-partner liquidity layer. raSOL is in your wallet, controlled by your keys. You can swap on any DEX, route through Sanctum directly, or hold the position regardless of Hubra's app status. # raUSDC Source: https://docs.hubra.app/general/raUSDC Token reference for the Hubra Earn USDC vault receipt. raUSDC is the receipt token for the Hubra Earn USDC vault. This page is the **token reference**: addresses, mechanics, and what to do (and not to do) with it. For the user-facing product story, see [USDC Earn](/overview/usdc-earn). *** ## Token data | | | | -------------- | ------------------------------------------------------------------------------------------------------------------------------- | | Name | Hubra Earn USDC | | Receipt symbol | raUSDC | | Standard | SPL Token (classic, not Token-2022) | | Receipt mint | `53fZaJGDMHcfku8pzZak5obVFUUjVxwqRTF63M3SQiSS` | | Decimals | 9 | | Underlying | USDC (`EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`) | | Vault address | `3maCuTJVPteZ2dFA8dADxz2EbpJHfoAG5txYhXDs6gNQ` | | Infrastructure | Voltr | | Icon | [Arweave](https://7tjb2jhu6znbujvdbauzmsyhqtp42yedjgncsnsdphwtje7ukszq.arweave.net/_NIdJPT2WhomowgplksHhN_NYINJmik2Q3ntNJP0VLM) | raUSDC has **9 decimals**, even though USDC has 6. Voltr vaults mint shares at 9 decimals regardless of the underlying asset. When reading raUSDC balances directly from the chain, scale by `10^9`. The Hubra app surfaces USDC-denominated values to hide this asymmetry. *** ## Token model Same shape as [raSOL](/general/raSOL): non-rebasing, value-accruing. * Your raUSDC balance is fixed at mint. * The redemption rate (USDC per raUSDC) climbs as the underlying vault earns yield. ``` raUSDC_balance × current_rate = your_underlying_USDC ``` The Hubra app does this math for you and displays your USDC value directly. You only see raUSDC if you connect an external wallet. *** ## Where the yield comes from The Voltr vault routes USDC across audited Solana lending venues (Kamino, Jupiter, and the whitelisted set). Yield from those venues flows into the raUSDC redemption rate. There is no Hubra fee on the position. The displayed APY is the venue-blended yield net of any underlying market spreads. *** ## Working with raUSDC raUSDC is a standard SPL token. You can hold, transfer, or trade it like any other SPL token, and the claim on your vault position moves with the token. **Do not burn your raUSDC.** Burning it destroys the receipt and permanently forfeits access to your position. Transferring and trading are safe; only burning is irreversible. If you connect an external wallet (Phantom, Backpack, Solflare) and deposit into Earn, raUSDC appears in that wallet. To redeem it for USDC, use the Hubra app. *** ## Working with raUSDC programmatically * Build a deposit (USDC → raUSDC): [`POST /api/v1/stake`](/developer/endpoints/stake) with `strategy: "usdc-earn"`. * Build a withdraw (raUSDC → USDC): [`POST /api/v1/unstake`](/developer/endpoints/unstake) with `strategy: "usdc-earn"` and `kind: "instant"`. * Get live vault stats: [`GET /api/v1/strategies/usdc-earn`](/developer/endpoints/get-strategy). *** ## Risks * **Vault smart-contract risk.** Voltr's vault contracts have been audited but no contract is risk-free. * **Adapter risk.** Each integration (Kamino, Jupiter, etc.) carries its own contract surface. * **Underlying venue risk.** A failure of an integrated lending market could affect the position. * **Stablecoin risk.** USDC is a Circle-issued stablecoin and carries the issuer risk associated with that. See [USDC Earn](/overview/usdc-earn) for the full risk discussion. # Hubra Source: https://docs.hubra.app/index Stake SOL with a six-year Solana validator. Native or liquid. Slow or fast. Hubra Documentation and developer reference for the Hubra staking surface. Pick a feature to read about, or jump to the [Developer](/developer/overview) tab for the agent-callable HTTP API. Delegate SOL directly to Hubra's validator. Custody stays with your wallet. Mint raSOL and keep your stake usable across Solana DeFi. Exit native or liquid positions in a single transaction. Stablecoin yield from the same operator, routed through audited venues. *** ## Two surfaces, one operator Hubra is built for two audiences in parallel: * **Humans** use the app at [hubra.app](https://hubra.app/s). Wallet connect, two-click strategy choice, gasless transactions. * **Agents** use the [Hubra Agent API](/developer/overview). Plain HTTPS, no API key, unsigned transactions returned for the agent to sign locally. Both surfaces call the same underlying server actions, so an agent and a user staking the same amount get an identical on-chain result. *** ## Public receipts | | | | ---------------------- | ---------------------------------------------- | | Validator vote account | `7K8DVxtNJGnMtUY1CQJT5jcs8sFGSZTDiG7kowvFpECh` | | raSOL mint | `HUBsveNpjo5pWqNkH57QzxjQASdTVXcSK7bVKTSZtcSX` | | raUSDC mint | `53fZaJGDMHcfku8pzZak5obVFUUjVxwqRTF63M3SQiSS` | | Voltr USDC vault | `3maCuTJVPteZ2dFA8dADxz2EbpJHfoAG5txYhXDs6gNQ` | Verify the validator on Solscan: [vote account](https://solscan.io/account/7K8DVxtNJGnMtUY1CQJT5jcs8sFGSZTDiG7kowvFpECh). *** ## Where to go next Five minutes from "what is this" to "I have raSOL in my wallet". The agent-callable HTTP API. No key required. For the broader product story (who Hubra is, the validator philosophy, the operator record), visit [hubra.app](https://hubra.app). # Privacy policy Source: https://docs.hubra.app/legal/privacy-policy # Hubra Privacy Policy This Privacy Policy explains how **Hubra** (the “Website”, the “App”, or “Hubra”) collects, uses, stores, and discloses information when you use Hubra and the related content, services, and applications made available through it (collectively, the “Services”). Hubra was developed by a dedicated member of the Solana community and his company (the “Developer”). By accessing or using the Services, you confirm that you have read, understood, and agree to the practices described in this Privacy Policy. **If you do not agree, do not use the Services.** This Privacy Policy should be read together with the [Hubra Terms and Conditions](/legal/terms-and-conditions). *** ### Our Approach Hubra is a **non-custodial platform aggregator** for the Solana ecosystem. We do not operate accounts in the traditional sense — you interact with the Services through your own self-custodial Solana wallet. We collect the **minimum** information needed to operate the Services, calculate your Points, and improve the product. We **never** sell your data, and we never ask for or receive your private keys or recovery phrases. *** ### Information We Collect #### Wallet address When you connect a Solana wallet, we collect and store your **public wallet address** (a base58 string). We use it to recognize you across sessions, read your on-chain holdings, calculate Points, and operate features such as referrals. We also record the **name of the wallet provider** you connect with (for example, Phantom or Solflare) for analytics attribution. #### On-chain holdings snapshots To calculate Points and show your portfolio over time, we periodically read **public, on-chain information** associated with your wallet address and store daily snapshots of your holdings, including: * Native SOL stake amounts * Liquid staking (raSOL) balances * Leveraged staking (raSOL Max) positions * USDC / raUSDC balances * The computed USD value of those holdings at snapshot time This information is **read from the public Solana blockchain** — it is not transmitted to us from inside your wallet. #### Points and referrals We store your **Points history** (daily and cumulative totals, the per-product breakdown used to calculate them, and your tier). If you create or apply a referral code, we store the **relationship between your wallet and your referrer’s wallet**, along with your referral code. #### Activity records When you complete an action through the Services (such as stake, unstake, claim, or restake), we may store a record of that action — the action type, asset, strategy, amount, and the public transaction signature — to power your activity feed. #### Push notifications (optional) If you choose to enable browser push notifications, we store the **push subscription endpoint** and the associated **encryption keys** provided by your browser, so we can deliver notifications you have requested. Enabling push notifications requires you to sign a message with your wallet to prove ownership of the address. #### Newsletter (optional) If you choose to subscribe to our newsletter, we collect the **email address** you provide and your marketing preference. Email addresses are handled by our email provider (see “Third-Party Services” below). #### Technical information We collect your browser’s **User-Agent string** when you connect a wallet or subscribe to push notifications. We use it for support, troubleshooting, and to avoid duplicate notification subscriptions. We also use privacy-respecting analytics (see “Analytics” below) to understand aggregate, non-identifying usage of the Services. *** ### Information We Do **Not** Collect We want to be explicit about what Hubra never collects or has access to: * **Private keys, secret keys, or recovery (seed) phrases.** All transaction and message signing happens entirely within your own wallet. Hubra never sees this material. * **IP-based geolocation or precise location data.** * **Device fingerprints** or persistent device identifiers beyond the User-Agent described above. * **Cookies.** The Services do not set tracking cookies. * **Government identifiers, health data, biometric data, financial-account credentials, or other sensitive “Regulated Data”** as defined by applicable data-protection laws. *** ### How We Use Information We use the information we collect to: * Operate, maintain, and provide the Services; * Read your on-chain holdings and calculate and display your Points and tier; * Operate the referral program; * Build transactions for you to review and sign, and broadcast transactions you have signed; * Deliver push notifications you have requested; * Send the newsletter you have subscribed to; * Provide support and troubleshoot issues; and * Understand aggregate usage and improve the product. We do **not** use your information for advertising, and we do **not** sell or rent it to third parties. *** ### Analytics We use **Vercel Analytics** and **Vercel Speed Insights** to measure page views and performance metrics (such as Core Web Vitals). We also record **anonymized event funnels** — for example, which wallet provider was used, whether an action succeeded or failed, and amount *ranges* (buckets) rather than exact figures. These events **do not include your wallet address or your exact transaction amounts.** *** ### Third-Party Services The Services integrate with third parties to function. Depending on the actions you take, your **public wallet address** and the relevant transaction details (for example, token mints and amounts) may be shared with: * **Solana RPC providers** — to read on-chain data and broadcast transactions; * **Sanctum** — to route liquid-staking token swaps; * **Voltr** — to operate the USDC Earn vault; * **Jito** — to submit transactions with MEV protection; * **Firebase Cloud Messaging (Google)** — to deliver push notifications, if you enable them; * **Resend** — to manage newsletter email, if you subscribe; and * **Vercel** — for hosting and the analytics described above. We share only what is necessary for each service to perform its function, and we never share private keys or recovery phrases (we do not have them). We require third-party services that are integrated with the Services and that may access your information to handle it in a manner consistent with the **Solana Mobile Publisher Policy** and applicable law. Each third-party service is also governed by its own terms and privacy policy, which we encourage you to review. *** ### Data Security We use encryption in transit (HTTPS/TLS) and at rest for the information we store, and we follow industry best practices to protect it. Access to stored data is restricted. No method of transmission or storage is completely secure, however, and we cannot guarantee absolute security. We collect only the minimum information required to provide the Services, and we never offer your information for sale. *** ### Data Retention We retain wallet addresses, holdings snapshots, Points history, referral relationships, and activity records for as long as needed to provide the Services and maintain historical features such as your Points trajectory, or as required for legitimate purposes such as fraud prevention or compliance with applicable law. Push subscriptions are retained until you unsubscribe. *** ### Your Choices and Consent Your use of data-collecting features is **opt-in**, and we respect your decision to decline: * You choose whether to **connect a wallet**. You can disconnect at any time. * **Push notifications** are off until you enable them, and you can turn them off at any time from your browser or device or by unsubscribing in the App. * The **newsletter** is opt-in, and every email includes a way to unsubscribe. We do not use fraudulent, deceptive, or coercive measures to obtain your consent to collect, share, or use your information. *** ### Data Deletion You may request deletion of the information associated with your wallet address — including holdings snapshots, Points history, referral relationships, activity records, and push subscriptions — by emailing us at [hello@hubra.app](mailto:hello@hubra.app). We may ask you to verify ownership of the wallet before acting on the request. We will delete the requested information except where we have a lawful reason to retain it, such as regulatory requirements or fraud prevention. Please note that information recorded on the **public Solana blockchain** is outside Hubra’s control and cannot be altered or removed by us. *** ### Children’s Privacy The Services are intended only for users who are **18 years or older**, or who meet the age of legal consent in their jurisdiction. We do not knowingly collect information from minors. If we learn that we have collected information from a minor without the consent of a parent or guardian, we will delete it. If you believe a minor has provided us information, please contact us at [hello@hubra.app](mailto:hello@hubra.app). *** ### International Users The Services are operated for a global audience, and your information may be processed in countries other than the one in which you reside. By using the Services, you consent to such processing in accordance with this Privacy Policy and applicable law. *** ### Changes to This Privacy Policy The Developer may update this Privacy Policy from time to time. Material changes will be reflected by updating the “Last updated” date below. Your continued use of the Services after a change takes effect constitutes acceptance of the revised Privacy Policy. *** ### Contact For questions or requests regarding this Privacy Policy or your information, please contact: **Email:** [hello@hubra.app](mailto:hello@hubra.app) *** Last updated: June 2026 # Terms and conditions Source: https://docs.hubra.app/legal/terms-and-conditions # Hubra Terms and Conditions Welcome, and thank you for choosing to visit **Hubra.app** (the “Website” or “Hubra”). These Terms of Service (“Terms”) govern the use of the Website and the related content, services, and applications made available through the Website (collectively, the “Services”). By accessing and/or using the Website and/or the Services, you (“you” or “User”) confirm and agree that you have read, understood, accepted, and agreed to be bound by these Terms.\ **If you do not agree to these Terms, do not use the Website or the Services.** *** ### What is Hubra? Hubra was developed by a dedicated member of the Solana community and his company (the “Developer”). It is designed to help Solana users: * Interact with protocols and DeFi apps more easily, * Gain a clearer view of their activities and assets, and * Serve as a one-stop hub for interacting with the Solana ecosystem. #### Service Model: * Some Hubra services are **open-source and freely available**. * Other services may incur **affiliate fees** or **transaction approval charges**, as specified under each service. #### Key Considerations: * Hubra is a **platform aggregator** and not a provider of commercial, financial, or professional services. * All actions performed on Hubra are carried out via **your own wallet**. * You can stop using Hubra anytime without any effect on your transactions or assets. *** ### Fees and Payments #### Applicable Fees * Services may incur **fixed fees**, **percentage-based fees**, or fees **determined dynamically** based on network conditions. * Fee structures will always be disclosed before you approve any transaction. #### Fee Disclosure * Before confirming a transaction, all applicable fees (including network or gas fees) will be displayed. * By approving a transaction, you agree to pay the specified fees. #### Non-Refundable Nature * All fees are non-refundable unless explicitly stated. #### Third-Party Fees * Third-party transactions may include additional fees. Hubra does not control or manage these charges, and you are responsible for reviewing them. *** ### Changes to Terms and Services * The Developer reserves the right to modify these Terms at any time. * Continued use of the Services implies acceptance of the revised Terms. * The Developer may: * Change or discontinue the Services. * Impose conditions, limitations, or fees. * Take these actions without prior notice or liability. *** ### Restrictions #### Eligibility * Users must be **18 years or older** or meet the age of legal consent in their jurisdiction. * Users from **high-risk jurisdictions**, as defined by the Financial Action Task Force (FATF), are prohibited. #### Wallet Usage * To use the Services, a compatible **Solana blockchain wallet** is required. * Wallets are provided by third-party providers, and their use is subject to their terms. * Hubra does not have access to your wallet, assets, or private details (only the public wallet address is accessed). *** ### Website Content * The Website’s functionality, design, and explanatory features were developed by the Developer and may change over time. * All data displayed is sourced from the **blockchain** or **third-party services**. * Hubra does not guarantee the accuracy or completeness of the data displayed. *** ### Hubra Services and Third-Party Services #### Hubra * Hubra aggregates **third-party services** operating on the Solana blockchain. * It acts as an interface, allowing you to access and transact with these services via your wallet. #### Third-Party Services * Hubra is not responsible for the functionality, accuracy, or outcomes of third-party services. * Use of third-party services is at your own risk, and you must review their terms independently. *** ### General Terms and Disclaimers * Hubra provides only technological tools and does not offer any advice or assurance regarding your activities. * The Developer has no access to your accounts or digital assets. * The Services are not guaranteed to function without disruptions, delays, or errors. *** ### Risk Disclosures * **Volatility**: Cryptographic assets are highly volatile and may result in financial loss. * **Smart Contract Risks**: Transactions depend on third-party smart contracts. Hubra does not guarantee their functionality. * **Transaction Irreversibility**: Blockchain transactions are final and irreversible. *** ### Intellectual Property * The Developer owns all intellectual property rights to the Website and Services. * A limited, non-transferable license is granted to access and use the Services under these Terms. *** ### Privacy and Data Use * Hubra only accesses **public blockchain information** linked to your wallet. * Transactions involving third-party services are governed by their respective privacy policies. *** ### Legal Compliance and User Responsibilities * Users must comply with applicable laws, including **anti-money laundering (AML)** and **economic sanctions** regulations. * Prohibited activities include: * Illegal transactions. * Fraudulent, malicious, or deceptive activities. *** ### Disclaimer of Warranties and Liability * The Services are provided **"as is"** without any warranties. * The Developer is not liable for indirect, incidental, or consequential losses arising from your use of the Services. *** # Affiliate Terms and Conditions These Affiliate Terms and Conditions (“Terms”) govern your participation in the Affiliate Program for Hubra’s **Stash** application, an aggregator of Solana’s third-party applications utilizing third-party smart contracts (the “Program”). By generating or sharing an affiliate URL, you (“Affiliate”) agree to be bound by these Terms. *** #### **Eligibility** * You must be at least **18 years old** and have a valid **Solana wallet** to participate. * Hubra reserves the right to refuse or terminate your participation at its sole discretion. *** #### **Affiliate URL and Affiliate Fees** * Access the **"Earn Referral Fees"** section within the Stash application to generate your unique affiliate URL (“Affiliate URL”). * You will earn a percentage of Hubra platform fees associated with transactions completed by referred users accessing the Stash application through your affiliate URL (“Affiliate Fees”). * The percentage and details of the Affiliate Fees depend on the affiliate plan tied to your unique URL. Specific plan details will be disclosed during URL generation. * Hubra reserves the right to update the terms of affiliate plans without prior notice. *** #### **Platform Fees and Affiliate Payments** * Platform fees are displayed to users during transaction approval within the Stash application. * Affiliate Fees are calculated automatically and transferred directly to the affiliate’s wallet via the Stash Program. These payments are not processed directly by Hubra. * Transactions are **final and automated** upon execution by the Stash Program. Hubra is not responsible for: * Delays or errors due to network congestion. * Incorrect wallet addresses provided by the Affiliate. *** #### **Prohibited Activities** Affiliates must **not**: * Use fraudulent, misleading, or deceptive methods to generate traffic or transactions. * Engage in spam, unauthorized advertising, or activities damaging to Hubra reputation or violating applicable laws. * Manipulate transactions or abuse the Program, including **self-referrals** or **multiple accounts** to earn Affiliate Fees. Violations will result in **immediate termination** from the Program, forfeiture of unpaid earnings, and potential legal remedies. *** #### **Affiliate Responsibilities** * Affiliates are responsible for complying with all **applicable laws and regulations**, including tax obligations arising from affiliate earnings. * Hubra is not responsible for any **tax liabilities** or legal obligations incurred due to participation in the Program. *** #### **Program Modifications and Termination** * Hubra reserves the right to **modify**, **suspend**, or **terminate** the Program or these Terms at any time, with or without notice. *** #### **Limitation of Liability** * Hubra is not liable for any loss or damage, including financial loss, resulting from participation in the Program. * Hubra does not guarantee uninterrupted operation of the Program or smart contracts and is not responsible for disruptions, errors, or failures. *** #### **Acceptance of Terms** By generating or sharing an affiliate URL, you acknowledge that you have read, understood, and agree to be bound by these Terms. *** *** ### Restrictions #### Eligibility * Users must be **18 years or older** or meet the age of legal consent in their jurisdiction. * Users from **high-risk jurisdictions**, as defined by the Financial Action Task Force (FATF) and the European Union (EU), are prohibited. #### Combined Blocking List To align with both FATF and EU requirements, the following jurisdictions are blocked from using Hubra: **FATF Blacklist** * North Korea * Iran * Myanmar **EU High-Risk Jurisdictions (broader scope)** * Afghanistan * Algeria * Angola * Burkina Faso * Cameroon * Democratic Republic of the Congo * Haiti * Iran *(duplicate with FATF)* * Ivory Coast (Côte d'Ivoire) * Kenya * Laos * Lebanon * Mali * Monaco * Mozambique * Myanmar *(duplicate with FATF)* * Namibia * Nepal * Nigeria * North Korea *(duplicate with FATF)* * South Africa * South Sudan * Syria * Tanzania * Trinidad and Tobago * Vanuatu * Venezuela * Vietnam * Yemen If you reside in or access the Services from these jurisdictions, you are not eligible to use Hubra. #### **Contact Information** For inquiries regarding these Terms or the Affiliate Program, please contact:\ **Email:** [hello@hubra.app](mailto:hello@hubra.app) *** Last edited on Sept 2025 # Instant unstake Source: https://docs.hubra.app/overview/instant-unstake Exit a SOL position in a single transaction. Native or liquid, full or partial. Sanctum-routed. Solana's stake program enforces a 2 to 3 day deactivation period. Hubra's instant unstake routes around it through Sanctum's shared LST liquidity layer, so you can exit a position in a single block instead of waiting an epoch. This page covers what's actually happening on-chain, which Sanctum endpoint each path uses, and how to wire the calls programmatically. Hubra charges no protocol fee on unstake. Cost is **price impact only**, dynamic and quoted live by Sanctum, starting from around 0.05% in healthy markets. *** ## What it covers | Source | Output | Path | Sanctum endpoint | | ---------------------------- | --------------------- | ---------------------- | --------------------------------------------- | | Active native stake account | SOL | One transaction | `swap/depositStake` | | raSOL | SOL | One transaction | `swap/token` (instant) | | raSOL | SOL via stake account | Two phases (\~2 to 3d) | `swap/withdrawStake` (slow) | | Native stake account (slice) | SOL | Split + route | `StakeProgram.split` then `swap/depositStake` | | raUSDC | USDC | Vault direct-withdraw | Voltr `direct-withdraw` (no Sanctum) | The first three are the Sanctum-routed flows. The last one is included because USDC withdraws are also "instant"; they just don't pass through Sanctum. *** ## How it works under the hood ### Native instant: `depositStake` Hubra calls Sanctum's order endpoint: ``` POST https://swap.sanctum.so/v1/swap/depositStake/order { "wallet": "", "stakeAccount": "", "outToken": "So11111111111111111111111111111111111111112" } ``` Sanctum returns an order response with: * `tx` — the unsigned transaction (server-normalized to standard base64 by Hubra). * `outAmt` — the SOL output in lamports. * `swapSrcData.data.priceImpactPct` — the price impact as a fraction. The transaction does the following on-chain: 1. Re-authorizes the stake account so Sanctum's pool can claim it (`StakeProgram.authorize`). 2. Calls Sanctum's `depositStake` instruction. The pool absorbs the active stake and mints SOL to the wallet at the pool's current rate. 3. The pool runs deactivation in the background; you have already exited. `depositStake` consumes the **entire** stake account. It does not take an amount; partial exits require splitting first (see below). ### Liquid instant: pooled token swap Same shape, different Sanctum endpoint: ``` POST https://swap.sanctum.so/v1/swap/token/order { "wallet": "", "inToken": "HUBsveNpjo5pWqNkH57QzxjQASdTVXcSK7bVKTSZtcSX", // raSOL "outToken": "So11111111111111111111111111111111111111112", // SOL "amount": "" } ``` The transaction is a routed swap across Sanctum's pooled LST liquidity. Because raSOL is a Sanctum preferred-partner LST, the pool depth is among the deepest on Solana — price impact at typical sizes is materially lower than `depositStake` at the same SOL notional. ### Liquid slow: `withdrawStake` For when you want zero price impact and can wait an epoch: ``` POST https://swap.sanctum.so/v1/swap/withdrawStake/order { "wallet": "", "inToken": "HUBsveNpjo5pWqNkH57QzxjQASdTVXcSK7bVKTSZtcSX", "voteAccount": "7K8DVxtNJGnMtUY1CQJT5jcs8sFGSZTDiG7kowvFpECh", "amount": "" } ``` The transaction: 1. Burns your raSOL. 2. Creates a fresh native stake account, delegated to Hubra's validator, funded with the underlying SOL. 3. Returns control of the stake account to your wallet. You then run `StakeProgram.deactivate` manually (or via [`POST /api/v1/unstake`](/developer/endpoints/unstake) with `kind: "deactivate"`), wait for the deactivation epoch, and call [`POST /api/v1/withdraw`](/developer/endpoints/withdraw) to close the account. No fee, no price impact. Costs the same epoch wait as native staking. *** ## Partial unstake on native: how the split works `depositStake` is all-or-nothing. To exit only part of a native stake account, the source account is **split first**. ``` existing stake account (10 SOL active) │ │ StakeProgram.split ▼ ┌───────────────────┬───────────────────┐ │ slice (3 SOL) │ remainder (7 SOL)│ │ ↓ depositStake │ keeps earning │ │ SOL out │ │ └───────────────────┴───────────────────┘ ``` On-chain, the split is a single instruction (`StakeProgram.split`) that creates a new stake account with the same delegation as the parent and the lamports you specified. The split account is `active` from the moment it exists; no re-activation epoch is needed. The split account is what gets routed through `depositStake`. The remaining stake account keeps earning rewards as if nothing happened. ### Costs | | | | --------------------------------- | ---------------- | | Rent for the new (split) account | \~0.002 SOL | | Sanctum price impact on the slice | varies | | Hubra protocol fee | None | | Network fees | Covered by Hubra | In the app, the split + route happens inside a single transaction-card flow; you see one quote and one signature. *** ## The order response and `sanctum_order` Every Sanctum-routed build response carries the **original Sanctum order** alongside the unsigned transaction: ```json theme={null} { "transaction": "", "hubra_token": "", "route": "sanctum", "sanctumKind": "depositStake", "sanctum_order": { "inp": "...", "out": "...", "mode": "ExactIn", "inpAmt": "...", "outAmt": "...", "swapSrcData": { "...": "..." }, "tx": "..." }, "signers": [""] } ``` When broadcasting, the agent must forward `sanctum_order` (along with `sanctumKind`) back to [`POST /api/v1/broadcast`](/developer/endpoints/broadcast) when using `route: "sanctum"`. Sanctum's execute endpoint independently validates the signed transaction's message bytes against the original order and rejects mismatches — that is how the Sanctum router stays safe against tampering. If you broadcast via `route: "rpc"` (plain RPC), the chain itself does not need the order; the transaction is self-contained. You lose Sanctum's MEV-protected broadcaster, but the on-chain effect is identical. *** ## Sanctum kinds The `sanctumKind` field tells `/broadcast` which Sanctum execute endpoint to use: | `sanctumKind` | Used by | Sanctum execute endpoint | | --------------- | ---------------------------------------------- | ---------------------------- | | `token` | raSOL → SOL instant unstake; SOL → raSOL stake | `swap/token/execute` | | `depositStake` | Native instant unstake | `swap/depositStake/execute` | | `withdrawStake` | raSOL slow unstake | `swap/withdrawStake/execute` | | `depositSol` | (Reserved; not currently used by Hubra flows) | `swap/depositSol/execute` | Forward the `sanctumKind` from the build response to `/broadcast` verbatim. Mismatching it will return `400 invalid_request`. *** ## Quoting before you sign Always quote first for instant unstake at meaningful size. Pool depth changes minute to minute. ```bash theme={null} curl -X POST https://hubra.app/api/v1/quote \ -H 'Content-Type: application/json' \ -d '{ "strategy": "sol-liquid-stake", "wallet": "", "amount": "100" }' ``` Response: ```json theme={null} { "strategy": "sol-liquid-stake", "inAsset": "raSOL", "outAsset": "SOL", "inAmount": "100", "outAmount": "117.42", "priceImpactPct": 0.0028 } ``` For native instant, pass `stakeAccount` and the active stake amount in SOL: ```bash theme={null} curl -X POST https://hubra.app/api/v1/quote \ -H 'Content-Type: application/json' \ -d '{ "strategy": "sol-native-stake", "wallet": "", "stakeAccount": "", "amount": "1.0" }' ``` Quotes are **non-binding**. Pool state drifts between quote and broadcast; the actual `outAmt` may differ slightly. Treat the quote as a live estimate, not a contract. The quote endpoint reuses Sanctum's order endpoint server-side — the unsigned transaction the order also returns is discarded at the boundary. This is why the quote shape mirrors the build shape. Full reference: [`POST /api/v1/quote`](/developer/endpoints/quote). *** ## When instant pays vs. waiting Heuristics, not rules. Quote first either way. | Scenario | Recommendation | | ----------------------------- | -------------------------------------------------------------------------------------------------- | | \< 50 SOL, immediate need | Instant. Price impact is typically negligible. | | 50 to 500 SOL, time-sensitive | Instant. Quote first to confirm price impact is below your tolerance. | | 500 to 1000 SOL, patient | Slow (deactivate). Standard path, no price impact, no fee. | | 1000+ SOL, time-sensitive | Split across multiple instant unstakes over several hours, or split native:liquid. Quote each leg. | | 1000+ SOL, patient | Slow (deactivate). The economics dominate the timeline. | For raSOL specifically, pooled liquidity is deep enough that price impact stays low further up the size curve than native `depositStake`. This is the practical reason raSOL is the preferred entry point for users who anticipate possibly needing instant exit. *** ## Fees, in detail | Path | Hubra fee | Sanctum fee | Network fee | Other | | ---------------------- | --------- | ------------------------- | ---------------- | ------------------------------------------------ | | Native instant | None | Price impact (dynamic) | Covered by Hubra | — | | raSOL instant | None | Price impact (dynamic) | Covered by Hubra | — | | raSOL slow | None | None | Covered by Hubra | Epoch wait (\~2 to 3d) | | Partial native instant | None | Price impact on the slice | Covered by Hubra | \~0.002 SOL rent for the split account (covered) | | USDC instant | None | None | Covered by Hubra | — | Price impact comes from the depth of Sanctum's pool at the moment the swap runs. Hubra adds nothing on top. The number you see in the quote is what you pay. *** ## End-to-end example: raSOL instant unstake ```ts theme={null} import { VersionedTransaction, Keypair } from "@solana/web3.js"; // 1. Build const build = await fetch("https://hubra.app/api/v1/unstake", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ strategy: "sol-liquid-stake", wallet: wallet.publicKey.toBase58(), amount: "1.5", kind: "instant", }), }).then((r) => r.json()); const { transaction, hubra_token, route, sanctumKind, sanctum_order } = build; // 2. Sign const tx = VersionedTransaction.deserialize(Buffer.from(transaction, "base64")); tx.sign([wallet]); const signed = Buffer.from(tx.serialize()).toString("base64"); // 3. Broadcast through Sanctum (MEV-protected) const { signature } = await fetch("https://hubra.app/api/v1/broadcast", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ signed_tx: signed, hubra_token, route, sanctumKind, sanctum_order, }), }).then((r) => r.json()); console.log(`Confirmed: https://solscan.io/tx/${signature}`); ``` For native instant, replace `strategy` with `"sol-native-stake"` and add `stakeAccount`. The rest of the shape is identical. *** ## Common questions Sanctum's pool charges price impact for absorbing your stake or your raSOL. Hubra adds nothing on top. Larger unstakes consume more pool liquidity and incur higher impact. 0.01 SOL on the native and liquid paths. For partial native unstake, the remaining stake account must keep enough lamports to cover rent exemption (\~0.002 SOL). Single transaction. SOL arrives the moment the transaction confirms (typically a few seconds on Solana mainnet). Slow has zero cost. If you can wait 2 to 3 days, there is no reason to pay price impact. Instant exists for the cases where waiting is not an option. The unsigned transaction's blockhash expires (\~2 minutes), and so does the `hubra_token`. Rebuild via `/unstake` and try again. Sanctum order responses are short-lived for this reason. Yes — `route: "rpc"` works for any signed transaction. You lose Sanctum's MEV protection and smarter retries, but the on-chain effect is the same. Yes. [`POST /api/v1/unstake`](/developer/endpoints/unstake) with `kind: "instant"` returns the unsigned transaction; the agent signs locally and broadcasts. *** ## Get started Open the app, navigate to your position, choose instant. Build an unstake transaction programmatically. # Leveraged staking Source: https://docs.hubra.app/overview/leveraged-staking Auto-managed leveraged raSOL. Built on Voltr's audited vault program with Hubra's rasol-max adapter on Save × MarginFi. Leveraged staking is Hubra's amplified staking route. You deposit raSOL; the strategy posts it as collateral on Save Finance, borrows SOL against it, and atomically converts that borrowed SOL into more raSOL collateral through a flash-loan + Sanctum swap. The loop runs at a target leverage configured on the strategy, so your stake earns Solana validator rewards on a raSOL position larger than your principal - net of the SOL borrow cost. The receipt token is **raSOL Max**, an LP share of the levered position. Burn it at any time to redeem your raSOL at the current rate. No cooldown. Best when you want to amplify raSOL's staking yield and accept variable-borrow + liquidation risk. For unlevered exposure, use [Liquid staking](/overview/liquid-staking). *** ## How raSOL Max works Each leverage cycle is a single atomic transaction: ``` deposit raSOL ──► Save collateral │ ▼ ┌──────────────────────────────┐ │ 1. flash-borrow SOL │ ◄── MarginFi v2 (0 fee) │ 2. Sanctum-swap SOL → raSOL │ ◄── stake-pool-native rate │ 3. deposit raSOL on Save │ │ 4. borrow SOL on Save │ │ 5. repay flash with SOL │ └──────────────────────────────┘ │ ▼ more raSOL collateral, more SOL debt │ loop until target leverage ``` The position is held at a configured **target leverage** (max 2.75×). The adapter rebalances on its own: levers up when the SOL borrow rate sits comfortably below raSOL's staking yield, and pulls back when it spikes. There is nothing to manage from the user side. Swaps go through **Sanctum** - the router for SOL → raSOL and Sanctum Infinity (INF) for raSOL → SOL - which prices at the stake pool's native rate with no AMM spread. The flash bracket uses **MarginFi v2's** 0-fee flash loan, so the only cost per cycle is Sanctum's tiny slippage allowance (capped at 0.15%). ### Net yield ``` net_APY ≈ (raSOL_staking_APY × live_leverage) − (SOL_borrow_APY × (live_leverage − 1)) ``` `live_leverage` drifts inside a band around the target as rates and prices move. `SOL_borrow_APY` is the variable SOL borrow rate on Save's SOL reserve - that's what the levered SOL debt accrues at. The headline APY in the app is computed live from current strategy state and reflects both legs. ### raSOL Max token raSOL Max is the LP share of the vault. it can be used on defi like any other SPL | | | | ------------- | ---------------------------------------------------- | | Symbol | raSOL Max | | Underlying | raSOL | | Mints / burns | Voltr vault program | | Composability | Hubra app only - do not trade or transfer externally | The exchange rate `raSOL / raSOL Max` climbs over time as the levered position earns. Your raSOL Max balance stays fixed; redemption value grows. *** ## What's audited, what's not This is the part to read carefully. The vault program is audited. The Hubra **adapter** that drives the leverage loop is **not yet audited**. Treat raSOL Max as experimental and only deposit what you can afford to expose to smart-contract risk. ### Vault layer - audited The vault is built on the **[Voltr](https://voltr.xyz) vault program** - the same program family used by Hubra's [USDC Earn](/overview/usdc-earn) product. Voltr's vault program has been audited by FYEO and secured by Sec3's X-RAY static analysis tool. | Component | Reviewed by | Status | | ------------------- | --------------------------------- | ------ | | Voltr vault program | FYEO (audit) | Passed | | Voltr vault program | Sec3 X-RAY (static analysis tool) | Passed | Sec3 X-RAY scanner software is a security scanner specifically designed for Solana smart contracts. Sec3 X-RAY can detect many different types of security vulnerabilities and is integrated into our development process. Sec3 X-RAY has been adopted at leading Solana Protocols. The vault owns the raSOL deposit, owns the raSOL Max mint, enforces the share-price math on deposit/withdraw, and only allows whitelisted adapters to act on the assets it holds. ### Adapter layer - not audited The leverage loop itself lives in Hubra's **`rasol-max` adapter** - the program that the vault calls into to deposit raSOL on Save, flash-borrow SOL from MarginFi, Sanctum-swap between SOL and raSOL, and rebalance the position. The adapter is whitelisted on the vault, but the adapter code has **not been independently audited**. What the adapter can and cannot do is bounded by the vault: * It can route vault assets only to the configured venues (Save, MarginFi, Sanctum). * It cannot move assets to arbitrary destinations - the vault's whitelist is enforced on every call. * It cannot mint or burn raSOL Max outside the vault's share-price logic. That containment matters: a bug in the adapter can affect strategy performance (worse fills, mis-targeted leverage, stuck positions), but it cannot drain the vault to an external destination. Audit is still the right next step and is on the roadmap. *** ## Venues The strategy composes three external Solana protocols, each with a distinct role: | Venue | Role | Why | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | | **Save Finance** (main pool) | Lending venue. raSOL is the collateral reserve; SOL is the borrow reserve; the strategy owns a single Save obligation. | Deep raSOL collateral capacity and a SOL reserve with predictable rates. | | **MarginFi v2** (main group) | Flash-loan venue. Each lever-up / lever-down / redemption is wrapped in a `start_flashloan` / `end_flashloan` bracket, borrowing SOL for the duration of one transaction. | 0 protocol fee on flash loans (since 2026-05). The cheapest flash venue on Solana for SOL. | | **Sanctum** | Swap venue. Sanctum router for SOL → raSOL, Sanctum Infinity (INF) for raSOL → SOL. | Stake-pool-native pricing - no AMM spread, deeper than reserve-bounded direct withdrawals. | All three are public protocols with their own audit posture (each audited by multiple firms; see their respective documentation). Each carries its own protocol risk independent of Hubra or Voltr. The adapter never sends assets to a destination outside this set - the vault's whitelist enforces it on every call. *** ## Deposit and withdraw Both flows happen as single user-signed transactions through the SDK. ### Deposit You sign one transaction. The vault accepts your raSOL, the adapter routes it into the lending market and opens / extends the levered loop, and raSOL Max is minted to your wallet at the current share price. ``` raSOL_max_minted = raSOL_deposited × (lpSupply / tvlRasol) ``` When the vault is empty (bootstrap), the rate is 1:1. ### Withdraw You burn raSOL Max and receive raSOL back at the current rate. Two on-chain paths, chosen automatically by the SDK: | Path | When it runs | Shape | | --------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `vault-idle` | The vault has enough idle raSOL to cover the redemption directly (typical right after a fresh deposit, before the next lever-up cycle). | Single instruction. | | `flash-bracket` | The redemption requires unwinding a slice of the levered position. | 8–9 instructions wrapped in a MarginFi flash-loan bracket: borrow SOL, repay your share of Save's SOL debt, withdraw and redeem the matching raSOL collateral, Sanctum-swap raSOL → SOL to refund the flash, and park the residual raSOL as your payout. Uses an address lookup table to fit under Solana's 1232-byte tx envelope. | Both settle in one transaction. **No cooldown, no queue, no waiting period.** No manager or admin signature is required - you sign everything yourself. The first time you ever redeem, the SDK lazily prepends an init instruction to create your **per-user MarginFi account PDA** (a one-time rent of \~0.005 SOL). The PDA is deterministic from your wallet - no off-chain bookkeeping - and is reused on every subsequent redemption. ``` raSOL_returned = raSOL_max_burned × (tvlRasol / lpSupply) ``` *** ## Strategy state The strategy publishes its live state on-chain. Hubra's app reads it via the `@hubra-labs/rasol-max` SDK: | Field | Meaning | | -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | `tvlRasol` / `tvlSol` | NAV in raSOL lamports, plus its SOL-equivalent at the live stake-pool rate. | | `lpSupply` | raSOL Max outstanding. | | `navPerLp` | NAV per LP share (raSOL per raSOL Max, scaled). | | `obligationCollateralCtoken` | raSOL collateral posted on Save (in Save cToken units; appreciates over time as Save's raSOL reserve accrues). | | `obligationDebtSol` | SOL debt outstanding on Save's SOL reserve. | | `leverageBps` | Live leverage of the position (`collateral / equity`). | | `targetLeverageBps` | Configured target leverage. | | `maxLeverageBps` | Hard cap (≤ 27,500 = 2.75×). The strategy will never exceed this. | | `borrowAprBps` / `collateralSupplyAprBps` / `stakeYieldAprBps` | Live Save SOL borrow APR, raSOL supply APR, and the off-chain raSOL stake-pool yield premium - the three inputs the SDK uses to project net yield. | | `strategyApyBps` | SDK's simple linear net-APY estimate (pessimistic; ignores compounding). | | `paused` | When `true`, deposits and withdrawals are disabled. | The app surfaces the headline numbers; the SDK exposes the full state if you need to read it directly. *** ## Risks Leverage adds two risk dimensions on top of plain [Liquid staking](/overview/liquid-staking): * **Variable net yield.** When Save's SOL borrow rate rises, net APY shrinks - sometimes sharply. Yield can go negative for short windows if borrow cost exceeds leveraged staking yield. The rebalancer de-levers in that case, but not instantaneously. * **Liquidation risk.** Save uses the SOL/USD Pyth feed for both reserves and treats raSOL 1:1 with SOL in its LTV math. Sudden divergence between raSOL's market price and its stake-pool fair value, or oracle movement, can push the position toward liquidation. The strategy targets a leverage well below Save's liquidation threshold and rebalances actively, but liquidation is not impossible. * **Swap-rate risk.** Every lever-up / lever-down / redemption Sanctum-swaps between SOL and raSOL. Slippage is capped at 0.15% per swap, but persistent dislocation between raSOL and SOL can compound across cycles. * **Adapter smart-contract risk.** The `rasol-max` adapter is not audited. The vault containment limits blast radius, but adapter bugs can still degrade strategy performance. * **Vault smart-contract risk.** Audited by FYEO and secured by Sec3's X-RAY static analysis tool - but no contract is risk-free. * **Venue risk.** Save, MarginFi, and Sanctum each carry their own protocol risk independent of Hubra. * **Pause risk.** When the strategy is paused (admin response to a market or protocol event), deposits and withdrawals are temporarily disabled. The position itself continues to be managed. raSOL Max is an experimental, leveraged product. Only deposit what you can afford to expose to smart-contract and market risk. *** ## Comparing the routes | | Native | Liquid (raSOL) | Leverage (raSOL Max) | | --------------------- | --------- | -------------------------------------- | ---------------------------------------------------------------------------- | | Yield source | Validator | Validator | Validator × leverage − SOL borrow cost | | Smart-contract layers | None | Sanctum | Voltr vault + `rasol-max` adapter + Save + MarginFi (flash) + Sanctum (swap) | | Audit status | n/a | Sanctum audited | Vault audited; adapter not yet; Save, MarginFi, Sanctum each audited | | Withdraw speed | One epoch | One epoch (slow) / single tx (instant) | Single tx | | Variability | Low | Low | Higher - SOL borrow rates and leverage drift | | Liquidation risk | None | None | Yes (bounded by target leverage, Save LTV) | *** ## Common questions The Voltr vault program that holds your raSOL is audited by FYEO and secured by Sec3's X-RAY static analysis tool. The Hubra `rasol-max` adapter that drives the leverage loop is **not yet audited**. Audit is on the roadmap. No. The vault enforces a whitelist of destinations on every call. The adapter can only route vault assets into the configured lending markets and back. It cannot send assets to an arbitrary address. Net APY shrinks and can briefly go negative. The borrow cost in question is Save's variable SOL borrow rate - that's the liability side of the levered position. The rebalancer de-levers (via `rebalance_down`) when SOL borrow costs exceed leveraged staking yield by a healthy margin. The position stays manageable; the headline number on the page reflects the live reading. The strategy targets a configured leverage (read from the on-chain config). The live value drifts inside a band as rates and positions move. The hard cap (`maxLeverageBps`) is enforced by the strategy and cannot be exceeded. When the vault has enough idle raSOL, withdraw is a single instruction (`vault-idle`). When it needs to unwind a slice of the levered position, the SDK builds a MarginFi flash-loan-bracketed transaction (`flash-bracket`) with 8–9 instructions and an address lookup table. Both settle in one signature. The flash-loan bracket needs a MarginFi account whose authority is the user's wallet (MarginFi enforces this on the inner borrow/repay). The SDK derives a deterministic per-user PDA and lazily prepends an init instruction the first time you redeem through the flash path - a one-time rent of \~0.005 SOL. The PDA is reused on every subsequent redemption; no off-chain bookkeeping is involved. No. Deposit any amount of raSOL. A small amount for the signature itself. The flash-bracket withdraw is more compute-heavy than a plain transfer; budget accordingly. Plus the one-time \~0.005 SOL rent for your MarginFi PDA on first withdraw. *** ## Get started Open the app, connect a wallet, deposit raSOL. For unlevered exposure, see [Liquid staking](/overview/liquid-staking). For the raSOL token reference, see [raSOL](/general/raSOL). # Liquid staking Source: https://docs.hubra.app/overview/liquid-staking Mint raSOL for SOL staked with Hubra. Stake exposure, but transferable. Liquid staking gives you the same validator-backed yield as native staking, but the position is wrapped in a token you can move, swap, lend, or use as collateral. When you stake SOL through Hubra Liquid, you receive **raSOL**, Hubra's liquid staking token. Your raSOL balance stays fixed; the value of each raSOL grows as the underlying stake earns rewards. Best when you want validator-backed SOL exposure but also need liquidity or composability across Solana DeFi. *** ## How raSOL works raSOL is a **value-accruing** receipt token. The supply is fixed at the moment of mint; appreciation lives in the redemption rate, not the balance. ### The exchange-rate model When you stake, you receive raSOL at the current rate. Over time, as the underlying SOL earns staking rewards, the raSOL → SOL redemption rate climbs. ``` raSOL_balance × current_rate = your_underlying_SOL ``` You hold the same raSOL count, but each raSOL is worth more SOL over time. There is nothing to claim and nothing to compound manually. ### Mechanics * raSOL is minted via [Sanctum](https://sanctum.so), the unified LST infrastructure on Solana. * The underlying SOL is staked to the **Hubra validator** (vote identity `7K8DVxtNJGnMtUY1CQJT5jcs8sFGSZTDiG7kowvFpECh`). * Validator rewards flow into the raSOL exchange rate at every epoch boundary. raSOL mint address: `HUBsveNpjo5pWqNkH57QzxjQASdTVXcSK7bVKTSZtcSX`. Verify on [Solscan](https://solscan.io/token/HUBsveNpjo5pWqNkH57QzxjQASdTVXcSK7bVKTSZtcSX). *** ## Why liquid Native staking locks the position; liquid staking does not. With raSOL you can: * **Swap or sell** at any time on any Solana DEX (Jupiter, Orca, Meteora, Raydium, Titan). * **Use as collateral** on Kamino, Save, Loopscale. * **Provide liquidity** in raSOL pairs. * **Move it** between wallets and accounts as a normal SPL token. You keep validator-backed exposure even while raSOL is sitting in a lending market or LP. raSOL is a Sanctum preferred-partner LST. That means deeper pooled liquidity for swaps, instant unstake routes, and LST-to-LST conversions. *** ## Comparing native and liquid | | Native | Liquid (raSOL) | | ----------------------- | ------------------------------------ | ------------------------------------ | | Custody | Wallet | Wallet (raSOL is in the wallet) | | Smart-contract exposure | None | Sanctum | | Transferable | No | Yes (SPL token) | | Use in DeFi | No | Yes | | Activation delay | \~2 to 3 days | Instant (you receive raSOL on stake) | | Slow exit | \~2 to 3 days, no fee | \~2 to 3 days, no Hubra fee | | Fast exit | Sanctum `depositStake`, price impact | Sanctum swap, price impact | Both routes back the same validator. The choice is about whether you need composability. *** ## Where the yield comes from raSOL's APY is entirely Solana validator rewards. There is no second yield layer, no DeFi farming, no automated strategy. ``` raSOL_yield = validator_staking_rewards − pool_overhead ``` The number you see in the app is what the underlying stake account earns, surfaced as a redemption-rate climb. Live APY: [`GET /api/v1/strategies/sol-liquid-stake`](/developer/endpoints/get-strategy). *** ## Exiting Three paths: ### Instant (one transaction) raSOL → SOL via Sanctum's pooled LST liquidity. Settles in a block. Pays price impact only; Hubra charges no protocol fee. ### Slow (epoch-bounded, no fee) Sanctum's `withdrawStake` converts your raSOL into a native stake account. Then standard `StakeProgram.deactivate` runs the epoch cooldown. About 2 to 3 days total, no fee. ### Sell on a DEX raSOL is a standard SPL token. Swap it for SOL or any other token on any DEX you prefer. Treat it like any LST. See [Instant unstake](/overview/instant-unstake) for the full breakdown of the fast path. *** ## Risks Liquid staking adds a layer of complexity over native. Know the trade-offs: * **Smart-contract risk.** raSOL is minted via Sanctum infrastructure. Sanctum is one of Solana's most heavily used LST routers, but any contract can have bugs. * **Validator risk.** raSOL backs SOL staked to the Hubra validator. Validator downtime or underperformance lowers rewards. Hubra has a public uptime record since 2020. * **Temporary depeg.** In stressed markets, raSOL might trade below its fair SOL value on DEXs. This is normally short-lived. Instant unstake redeems at the true exchange rate even when the secondary market wobbles. * **Liquidity risk for instant unstake.** Pool depth determines price impact. Very large unstakes (1000+ SOL) may incur material slippage. Quote first via [`POST /api/v1/quote`](/developer/endpoints/quote). *** ## Common questions raSOL is non-rebasing. Your balance stays fixed, the redemption rate climbs. Multiply your balance by the current rate to see the SOL value. No. Stake any amount of SOL. No. Hubra covers all network fees on the staking and unstaking flows. Yes. raSOL is a standard SPL token. Use it on Kamino, Save, Loopscale, Jupiter, or any DEX. raSOL lives in your wallet. You own it regardless of whether Hubra is online. Swap on DEXs or use Sanctum directly to unstake. Same primitive (Solana liquid staking token), different validator backing. raSOL stakes to Hubra's validator; JitoSOL and mSOL stake to a delegation strategy across many validators. raSOL also runs entirely on Sanctum's preferred-partner liquidity layer. *** ## Get started Open the app, connect a wallet, choose Liquid. For programmatic flows, see [`POST /api/v1/stake`](/developer/endpoints/stake) with `strategy: "sol-liquid-stake"`. For raSOL technical reference, see [raSOL token](/general/raSOL). # Native staking Source: https://docs.hubra.app/overview/native-staking Delegate SOL directly to Hubra's validator. Custody stays with your wallet. Native staking is the cleanest delegation route on Solana. Your wallet creates a stake account, delegates voting rights to Hubra's validator, and remains in full custody throughout. There is no liquid token, no smart-contract layer, and no Hubra-side custody. Best when the principal prioritizes self-custody and minimal protocol surface. *** ## How it works A fresh Solana stake account is created from your wallet, funded with the amount you want to stake. The stake account is delegated to Hubra's validator at vote identity `7K8DVxtNJGnMtUY1CQJT5jcs8sFGSZTDiG7kowvFpECh`. Voting rights move to Hubra; everything else stays with the wallet. Stake activates at the next epoch boundary (typically within one epoch, about 2 to 3 days). Once active, it earns rewards each epoch. Solana's stake program adds epoch rewards directly to the stake account. There is nothing to claim, no harvest cycle, no transactions to sign. Compounding is built into the protocol. *** ## What you keep | | | | ----------------------- | --------------- | | Custody | Your wallet | | Stake authority | Your wallet | | Withdraw authority | Your wallet | | Voting rights | Hubra validator | | Smart-contract exposure | None | The wallet retains both stake authority and withdraw authority. Hubra cannot move, redirect, or claim the stake. The only thing the validator does is vote with the stake's weight. *** ## Rewards Solana validator rewards are protocol-paid: a share of network issuance distributed to active stake at the end of each epoch, minus the validator's commission. The math is simple: ``` your_reward = (your_active_stake / total_active_stake) × epoch_issuance × (1 - commission) ``` Hubra runs a low-commission validator. The exact APY moves with network inflation, validator performance, and stake-weight composition. Live numbers are visible in the [app](https://hubra.app/s) and via [`GET /api/v1/strategies/sol-native-stake`](/developer/endpoints/get-strategy). *** ## Exiting Two paths: ### Slow (epoch-bounded, no fee) `StakeProgram.deactivate` begins the cooldown. Once the deactivation epoch passes (\~2 to 3 days), the stake account is `inactive` and you can withdraw the SOL. This is the cleanest exit. No fee, no liquidity dependency, no third-party protocol. ### Fast (instant, pays price impact) Hubra routes the active stake account through Sanctum's `depositStake` flow. The active stake is exchanged for SOL in a single transaction. You pay a price-impact fee to Sanctum's LST liquidity pool, which depends on pool depth at the time of the trade. Useful when you cannot wait for an epoch. See [Instant unstake](/overview/instant-unstake) for the full mechanics. Partial unstake works too. The on-chain stake account is split first, the slice you specified is routed, and the rest stays earning. *** ## Why choose native Pick native when: * You want the smallest possible attack surface. Native staking touches the Solana stake program only. No Sanctum, no Voltr, no Hubra contract. * You want voting weight delegated explicitly to a single, identified validator. * You do not need raSOL as collateral, LP, or trade bait. Pick [liquid staking](/overview/liquid-staking) instead when you need a transferable receipt. *** ## Common questions No. Native staking delegates voting rights to Hubra's vote account. The stake account itself is created by, owned by, and controlled by your wallet. The withdraw authority never leaves your keys. The stake account begins voting at the next epoch boundary. Until then it is `activating` and earns no rewards. Solana's stake program enforces this. It is not a Hubra-imposed delay. Yes. Hubra routes through Sanctum's `depositStake` for instant settlement. Pays a small price-impact fee. See [Instant unstake](/overview/instant-unstake). Yes. The stake transaction, deactivate transaction, and withdraw transaction all run gasless on the Hubra app. You do not need a separate SOL balance for fees. Solana protocol penalties for downtime exist but are mild. A missed slot means a missed vote, which lowers stake-weighted rewards for that epoch. Hubra has a public uptime record stretching back to 2020. Yes. Native stake is portable. You can deactivate, then redelegate to any validator you choose. Your wallet keeps the stake account either way. *** ## Get started Open the app, connect a wallet, choose Native. For programmatic flows, see [`POST /api/v1/stake`](/developer/endpoints/stake) with `strategy: "sol-native-stake"`. # Quickstart Source: https://docs.hubra.app/overview/quickstart Stake your first SOL with Hubra in under five minutes. This guide gets you from a Solana wallet to a working stake position. No engineering required. Go to [hubra.app/s](https://hubra.app/s) and connect a Solana wallet (Phantom, Backpack, Solflare, or any other standard wallet). The app surfaces the four canonical paths. For SOL, pick **Native** for the cleanest custody surface or **Liquid** if you want a transferable raSOL receipt. For USDC, pick **Earn**. See [Strategies](/overview/native-staking) for the trade-offs. Type the amount of SOL or USDC you want to stake. The app shows the live exchange rate, expected output, and any fees up front. There is no minimum. Approve the transaction in your wallet. Hubra covers network fees, so you do not need a SOL balance set aside for gas. Your position shows up immediately on the **Positions** tab. For native stake, the position activates over the next epoch (about 2 to 3 days). For raSOL, the redemption rate begins climbing each epoch from the moment of mint. For USDC Earn, yield accrues continuously. *** ## When you want to exit Three paths, depending on the strategy: * **Native, slow:** Standard `StakeProgram.deactivate`. Account becomes withdrawable after the deactivation epoch (about 2 to 3 days). No fee. * **Native, instant:** Routed through Sanctum's `depositStake`. Active stake settles to SOL in one transaction. Pays a price-impact fee to the LST liquidity pool. * **Liquid, instant:** Swap raSOL → SOL through Sanctum's pooled LST liquidity. One transaction, pays price impact only. * **Liquid, slow:** `withdrawStake` to a native stake account, then standard epoch deactivation. * **USDC, instant:** Voltr direct withdraw. No cooldown, no fee. The full mechanics live in [Instant unstake](/overview/instant-unstake). *** ## What's next The cleanest delegation route. Stake exposure with a transferable receipt. Exit a position in a single transaction. Same operations, programmatic. # USDC Earn Source: https://docs.hubra.app/overview/usdc-earn Stablecoin yield from the Hubra validator operator. Voltr-routed, audited venues, instant withdraws. USDC Earn is Hubra's stablecoin route. You deposit USDC; the vault routes it across audited Solana lending venues for blended yield. You can withdraw any amount at any time, no cooldown, no fee. This page is the **feature reference**: how the vault is wired, what the on-chain accounts look like, and what the deposit / withdraw transactions actually do. *** ## Architecture ``` ┌──────────────────────────────┐ │ Hubra Earn (UI / API) │ └──────────────┬───────────────┘ │ Builds unsigned tx via │ ▼ ┌──────────────────────────────┐ │ Voltr API (api.voltr.xyz) │ │ /vault/{vault}/deposit │ │ /vault/{vault}/direct- │ │ withdraw │ └──────────────┬───────────────┘ │ On-chain call │ ▼ ┌──────────────────────────────┐ │ Voltr Vault Program │ │ 3maCu...6gNQ (vault PDA) │ └──────────────┬───────────────┘ │ Whitelisted adapters route to │ ┌──────────────────┼──────────────────┐ ▼ ▼ ▼ Kamino Jupiter (other) lending lending whitelisted ``` Hubra never holds USDC. The vault is a Solana program account governed by audited contracts; deposits and withdraws happen by the user's wallet calling the vault program directly. *** ## Vault metadata The full machine-readable metadata for the Hubra Earn USDC vault: ```json theme={null} { "name": "Hubra Earn USDC", "vaultAddress": "3maCuTJVPteZ2dFA8dADxz2EbpJHfoAG5txYhXDs6gNQ", "underlyingName": "USD coin", "assetSymbol": "USDC", "assetMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "assetTokenProgram": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "assetIcon": "/images/tokens/usdc.png", "receiptSymbol": "raUSDC", "receiptMint": "53fZaJGDMHcfku8pzZak5obVFUUjVxwqRTF63M3SQiSS", "icon": "https://7tjb2jhu6znbujvdbauzmsyhqtp42yedjgncsnsdphwtje7ukszq.arweave.net/_NIdJPT2WhomowgplksHhN_NYINJmik2Q3ntNJP0VLM", "decimals": 9 } ``` | Field | Value | Notes | | ------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `name` | Hubra Earn USDC | Display name. | | `vaultAddress` | `3maCu...6gNQ` | The Voltr vault PDA. The on-chain account that holds USDC and routes it to whitelisted adapters. | | `underlyingName` | USD coin | Long-form asset name. | | `assetSymbol` | USDC | The asset deposited and ultimately withdrawn. | | `assetMint` | `EPjFW...Dt1v` | Circle's USDC SPL mint on Solana mainnet. | | `assetTokenProgram` | `Tokenk...5DA` | Standard SPL Token program. The vault uses the classic SPL Token interface, not Token-2022. | | `assetIcon` | `/images/tokens/usdc.png` | Icon for the underlying asset. | | `receiptSymbol` | raUSDC | Symbol of the share token minted on deposit. | | `receiptMint` | `53fZa...QiSS` | The raUSDC SPL mint. Owned by the vault program. | | `icon` | Arweave URL | Icon for the receipt token. Hosted on Arweave for permanence. | | `decimals` | 9 | **raUSDC decimals.** Voltr vaults mint shares at 9 decimals regardless of the underlying asset's decimals (USDC is 6). The vault handles the unit conversion internally. | USDC has 6 decimals on-chain (`1.50 USDC = 1_500_000` base units). raUSDC has 9 decimals. When reading raUSDC balances directly from the chain, scale by `10^9`, not `10^6`. The Hubra app and API surface USDC-denominated values to hide this asymmetry. All four addresses are verifiable on Solscan. The vault is built on the **Voltr** vault program. The receipt token (raUSDC) is minted by the vault when you deposit, and burned by the vault when you withdraw. Hubra does not control the mint; the vault program does. *** ## Deposit flow (technical) ### What happens 1. Hubra's API calls `POST https://api.voltr.xyz/vault/3maCu...6gNQ/deposit`. 2. Voltr returns an unsigned base64 transaction. Hubra normalizes it to standard base64 and forwards. 3. The transaction includes: * SPL Token transfer instruction: USDC from your associated token account to the vault. * Voltr vault deposit instruction: mints raUSDC at the current share price. * (If needed) Associated token account creation for raUSDC. 4. Your wallet signs. 5. The transaction is broadcast. On confirmation, USDC is in the vault, raUSDC is in your wallet. ### Request shape (Voltr API, called server-side) ```json theme={null} { "userPubkey": "", "lamportAmount": "", "assetMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "assetTokenProgram": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" } ``` `lamportAmount` is the USDC amount in base units. With USDC decimals = 6, `1.50 USDC = 1_500_000` base units. Hubra's API takes a decimal string from the caller and converts to base units server-side. ### Share-price math raUSDC is minted at the current share price: ``` raUSDC_minted = USDC_deposited / share_price ``` Share price climbs over time as the vault's underlying allocations earn yield. Read it via Voltr's `/vault/{vault}/share-price` endpoint, surfaced through Hubra's [`GET /api/v1/strategies/usdc-earn`](/developer/endpoints/get-strategy). ### Programmatic deposit ```bash theme={null} curl -X POST https://hubra.app/api/v1/stake \ -H 'Content-Type: application/json' \ -d '{ "strategy": "usdc-earn", "wallet": "", "amount": "100" }' ``` Response: ```json theme={null} { "strategy": "usdc-earn", "transaction": "", "hubra_token": "", "route": "voltr", "signers": [""] } ``` Sign the transaction locally and broadcast via [`POST /api/v1/broadcast`](/developer/endpoints/broadcast) with `route: "rpc"`. There is no Sanctum involvement; plain RPC handles the broadcast. *** ## Withdraw flow (technical) USDC Earn supports **direct withdraw** only. Other Voltr vaults can have queued withdraws with a `withdrawalWaitingPeriod`; this one does not. ### Direct withdraw `POST https://api.voltr.xyz/vault/3maCu...6gNQ/direct-withdraw` returns an unsigned transaction that: 1. Burns your raUSDC. 2. Transfers USDC from the vault to your associated USDC token account. 3. Settles in a single transaction. No cooldown. ### `isWithdrawAll` semantics Two withdraw modes: | Mode | What it does | | --------------- | ------------------------------------------------------------------------------------------- | | Specific amount | Burns the exact raUSDC needed to redeem `amount` USDC at the current share price. | | Withdraw all | Burns your entire raUSDC balance. The `amount` field is ignored when `isWithdrawAll: true`. | Use `isWithdrawAll: true` when you want to drain the position fully without computing the exact raUSDC count. The vault handles the math. ### Programmatic withdraw ```bash theme={null} # Specific amount curl -X POST https://hubra.app/api/v1/unstake \ -H 'Content-Type: application/json' \ -d '{ "strategy": "usdc-earn", "wallet": "", "amount": "500", "kind": "instant" }' # Full withdraw curl -X POST https://hubra.app/api/v1/unstake \ -H 'Content-Type: application/json' \ -d '{ "strategy": "usdc-earn", "wallet": "", "kind": "instant", "isWithdrawAll": true }' ``` Both return `route: "voltr"`. Broadcast via `route: "rpc"`. *** ## Whitelisted adapters The Voltr vault program is configured with a whitelist of **adapters**: the only contracts the vault is allowed to deposit USDC into. Each adapter is independently audited. The current adapter set includes lending markets on: * **Kamino** — automated lending * **Jupiter** — lend infrastructure The full integration list is on-chain and exposed via Voltr's vault snapshot endpoint: ```bash theme={null} curl https://api.voltr.xyz/vault/3maCuTJVPteZ2dFA8dADxz2EbpJHfoAG5txYhXDs6gNQ ``` The `integrations` array in the response shows each `adaptorPk` (the adapter program account) and its `whitelisted` flag. The autonomous **rebalancer** decides where USDC moves between adapters based on relative yield. The rebalancer source code is public: Read the allocation logic on GitHub. *** ## Fee structure Voltr's vault fee config has four slots; for the Hubra Earn vault, each is set as follows: | Fee | Description | Hubra Earn value | | ---------------- | ------------------- | ---------------- | | `performanceFee` | Cut of yield earned | 0% | | `managementFee` | Annualized AUM fee | 0% | | `issuanceFee` | Charged on deposit | 0% | | `redemptionFee` | Charged on withdraw | 0% | The displayed APY is the venue-blended yield net of any underlying market spreads (e.g., Kamino's reserve fee on its lending markets). Those are not Hubra fees; they are baked into the upstream's quoted APY. Verify the fee config on-chain via `fetchUsdcVaultStats()` (`/vault/{vault}/feeConfiguration`). *** ## raUSDC: safe to move, never burn **Do not burn your raUSDC.** Burning it permanently forfeits access to your position. Transferring and trading the token are safe; only burning is irreversible. raUSDC is a standard SPL token, so you can hold, transfer, or trade it, and the claim on your vault position moves with it. It is not yet integrated into external DeFi protocols. Withdraws happen by redeeming raUSDC inside the vault, which converts it back to USDC at the current rate. If you connect an external wallet (Phantom, Backpack, Solflare) and deposit into Earn, raUSDC appears in that wallet. To redeem it for USDC, use the Hubra app. For the full token reference, see [raUSDC](/general/raUSDC). *** ## Reading vault state Three useful Voltr endpoints (called server-side by Hubra; surfaced through the agent API): | Voltr endpoint | Hubra surface | | ------------------------------------------- | ----------------------------------------------------------------------- | | `GET /vault/{vault}` | [`GET /api/v1/strategies/usdc-earn`](/developer/endpoints/get-strategy) | | `GET /vault/{vault}/share-price` | Embedded in `live.exchangeRate` | | `GET /vaults/user/{wallet}/interest-earned` | Surfaced in the Hubra app's portfolio view | The vault snapshot returns the full state: `totalValue`, `apy.{oneDay,sevenDays,thirtyDays,allTime}`, `dailyStats.apyData[]`, `allocations[]`, `integrations[]`, `feeConfiguration`. Hubra surfaces the headline numbers; the full snapshot is one HTTP call away if you need the rest. *** ## Risks USDC Earn is **distinct from validator staking risk**. You are exposed to: * **Vault smart-contract risk.** Voltr's vault program. Audited by FYEO and secured by Sec3's X-RAY static analysis tool, but no contract is risk-free. * **Adapter risk.** Each whitelisted adapter is its own contract surface. * **Underlying venue risk.** Kamino, Jupiter, etc. — each has its own audit and operational history. * **Stablecoin risk.** USDC is a Circle-issued stablecoin and carries the issuer risk associated with that. | Component | Reviewed by | Status | | ---------------- | --------------------------------- | ------ | | Vault program | FYEO (audit) | Passed | | Vault program | Sec3 X-RAY (static analysis tool) | Passed | | Adapter programs | Sec3 X-RAY (static analysis tool) | Passed | Sec3 X-RAY scanner software is a security scanner specifically designed for Solana smart contracts. Sec3 X-RAY can detect many different types of security vulnerabilities and is integrated into our development process. Sec3 X-RAY has been adopted at leading Solana Protocols. Only deposit what you can afford to expose to Solana DeFi risk. No yield product is fully risk-free. *** ## Common questions The transaction may include an associated token account (ATA) creation for raUSDC if your wallet has not held it before. ATA creation costs \~0.002 SOL of rent (covered by Hubra in the app, paid by the wallet for direct API callers). The current vault is USDC-only. Deposits in other tokens would need to be swapped to USDC first. Voltr's vault program supports both. The Hubra Earn vault is configured for direct (instant) withdraws — `withdrawalWaitingPeriod` is 0. A queued withdraw would require waiting `withdrawalWaitingPeriod` seconds before the funds settle. Not used here. The raUSDC redemption rate climbs continuously as the underlying allocations earn yield. Your raUSDC balance stays fixed; each unit becomes worth more USDC over time. No. Like any DeFi yield product, USDC Earn's principal carries smart-contract, protocol, and stablecoin risk. Call Voltr's vault snapshot endpoint and read the `allocations[]` array. Each entry shows the org name, strategy description, and `positionValue` of vault-deployed USDC at that venue. *** ## Get started Open the app and choose Earn. Build a deposit transaction programmatically.