Wallet providers
Build a self-custody Torq Earn program from product discovery through canonical deposit and redemption completion.
Build an Earn tab that stays inside your wallet, exposes institutional tokenized-credit products, and lets the user retain transaction control. This is a complete integration path, not an RPC or contract-call recipe.
Developer Preview. Obtain an approved test application, an origin-bound publishable key, the supplied SDK package, and Sepolia access before following the live steps.
Wallet-native Earn
Discovery, wallet consent, canonical completion
Load deep vault, market, liquidity, and eligibility state.
Show the exact product, amount, fees, and prepared intent.
The customer wallet simulates and submits the transaction.
Wait for final_success, then reread the position.
Catalog, vault detail, position, and redemption views.
Origin-bound challenge and short-lived customer authority.
Prepare, register, and canonical transaction tracking.
Signed events plus canonical resource rereads.
What you will build
- Earn catalog with asset, market, vault, allocation, liquidity, fee, activity, and risk details.
- Vault detail and eligibility view for the connected wallet.
- Origin-bound wallet session with no reusable custody secret in the browser.
partnerWrapperDepositandpartnerWrapperRedeemreview, prepare, simulate, sign, and track flows.- Position, activity, redemption, failure, and canonical completion states.
- Optional backend for signed webhooks, analytics, and recovery after the browser closes.
The user wallet is the customer-controlled signer. Torq never receives the private key or seed phrase, and the frontend never receives a Torq secret key.
Prerequisites
- A Torq Developer Preview organization, Sepolia application, approved wallet-provider program, allowed browser origin, and test publishable key.
- A supported EIP-1193 wallet or embedded wallet signer that can report chain ID, simulate where required, and submit exact prepared transactions.
- A server-side durable event inbox if users must resume workflows across devices or sessions.
- Product review for disclosures, eligibility, fees, redemption timing, and jurisdiction rules.
Keep TORQ_PUBLISHABLE_KEY origin-bound. Put TORQ_SECRET_KEY and TORQ_WEBHOOK_SECRET only in a
server secret store. Never copy them into frontend environment variables.
Project files
Start from the checked-in, dependency-free reference:
examples/developer-platform/earn-program-reference/
├── .env.example # documented server/test configuration
├── src/config.mjs # HTTPS and chain validation
├── src/torq-api.mjs # API and wallet-session client
├── src/catalog.mjs # complete catalog and deep product loader
├── src/workflows.mjs # prepare, sign, register, final_success
├── src/webhooks.mjs # raw-body signature verification
├── src/use-cases.mjs # wallet-provider boundary profile
└── test/reference.test.mjs # request and signature regressionsIn your application, split the browser and server boundaries explicitly:
src/earn/catalog.ts # browser: canonical read-only resources
src/earn/wallet-session.ts # browser: challenge and wallet signature
src/earn/workflow.ts # browser: proposal, prepare, simulate, submit
src/earn/views/ # catalog, vault, position, activity, redeem
server/torq/events.ts # server: signed webhook inbox
server/torq/reconcile.ts # server: canonical resource rereadsComplete data contract
Do not render every product with a generic sample object. Load the exact schemas from these API
pages and use returned freshness evidence in the UI:
| Purpose | Operations |
|---|---|
| Catalog | GET /v1/assets, GET /v1/markets, GET /v1/vaults, GET /v1/tranches, GET /v1/funds |
| Product detail | GET /v1/assets/{resourceId}, GET /v1/markets/{resourceId}, GET /v1/vaults/{resourceId} |
| Economics | GET /v1/vaults/{resourceId}/allocations, GET /v1/vaults/{resourceId}/liquidity, GET /v1/fees, GET /v1/partner-programs, GET /v1/wrappers |
| Activity and mobility | GET /v1/markets/{resourceId}/activity, GET /v1/vaults/{resourceId}/activity, GET /v1/vaults/{resourceId}/share-mobility |
| Customer state | GET /v1/positions, GET /v1/eligibility, GET /v1/activity |
| Workflow | POST /v1/workflows/prepare, POST /v1/workflows/{requestId}/transactions, GET /v1/workflows/{requestId} |
Complete credit lifecycle
Apply the complete credit lifecycle inside the same Earn tab:
| Customer moment | Wallet implementation |
|---|---|
| Deposit and tranche selection | Show Single Share, Senior, or Junior only when returned for the selected vault; review the wrapper deposit with the connected wallet. |
| Position, yield, and liquidity | Use server GraphQL vaultHolderPerformanceCurrent for the selected vault holder; keep vault detail, allocations, liquidity, and activity current, and treat GET /v1/positions only as additional wallet market-position context. |
| Redemption and claimable cash | Preserve the Torq redemption lifecycle and show immediate, queued, blocked, and claimable states instead of promising instant withdrawal. |
| Impairment monitoring | Reread GET /v1/liquidation-recovery, GET /v1/vaults/{resourceId}/activity, and the server event cursor when the selected vault or market changes materially. |
| Recovery center | Show GET /v1/funds, GET /v1/funds/{resourceId}, GET /v1/funds/{resourceId}/holdings, and wallet-specific recovery GraphQL beside the affected position. |
| Distressed-fund decision | Make it a separate capital allocation. An affected vault position does not automatically create eligibility, subscribe the wallet, compensate a loss, or create a fund claim. |
| Customer action | Require explicit customer approval in the same customer-controlled signer. If the approved Preview executor is absent, keep monitoring available and show capability_not_ready. |
| Final reconciliation | Keep the original vault position and optional fund position in separate UI and records; reconcile each from canonical state and signed events. |
Use src/credit-lifecycle.mjs from the reference implementation. GET /v1/events and
POST /graphql belong on the optional backend with a secret key; never expose them through the
browser publishable-key client.
Build the integration
1. Configure the browser client
Set the Sepolia chain ID, Torq API origin, approved application origin, and publishable key. Reject a mismatched API origin or chain before asking the wallet to sign anything. Create the Torq client once per origin and environment; never fall back from hosted to localhost.
2. Create a wallet session
Request POST /v1/wallet-sessions/challenges, display the Torq challenge unchanged, ask the user
wallet to sign it, then confirm with POST /v1/wallet-sessions/confirm. Validate the challenge
audience, API origin, chain, application, wallet, purpose, expiry, and nonce before signer invocation.
Use the returned short-lived wallet session only for its declared scopes.
3. Load the full Earn catalog
Call the list operations, then use loadEarnProduct() from src/catalog.mjs for the selected
asset, market, vault, and optional fund. Show the underlying asset, vault strategy, market exposure,
allocations, liquidity, fees, recent activity, share-mobility policy, freshness, and limitations.
Do not invent APY, liquidity, risk labels, or availability from partial fields.
4. Check the connected wallet
Read GET /v1/eligibility and GET /v1/positions using the wallet and selected vault. If the
resource is stale, the wallet is ineligible, share mobility is blocked, or the capability is not
ready, show the governed reason and keep the action disabled.
5. Build a human-readable proposal
For a deposit, propose workflow role.investor.partnerWrapperDeposit; for an exit, use
role.investor.partnerWrapperRedeem. Show product, asset, amount, wrapper, partner attribution,
estimated fees, signer, network, and possible approvals before calling prepare.
6. Prepare and review exact transaction intent
Send the typed body to POST /v1/workflows/prepare with a stable idempotency key. Require preflight
passed. Show every returned target, calldata summary, value, ordering, simulation requirement,
expiry, and blocked reason. The user must approve this exact prepared payload, not only the earlier
friendly proposal.
7. Simulate and submit with the user wallet
Reread GET /v1/workflows/{requestId} immediately before signing and confirm the snapshot and
transaction intents are unchanged. Verify chain and expiry. Simulate each required transaction, then
pass its exact to, data, and value to the user wallet in declared order.
8. Register and wait for canonical success
Register each hash through POST /v1/workflows/{requestId}/transactions. Poll the transaction
resource and workflow until both return final_success. A wallet receipt, confirmation count, or
event alone is not customer-visible completion. On success, reread positions, vault liquidity, and
activity before updating the UI.
9. Reconcile signed events
On your server, verify the timestamp, delivery ID, signature, and exact raw request bytes using
src/webhooks.mjs. Insert each delivery once, acknowledge quickly, then reread the referenced
canonical resource. Deduplicate delivery and event identifiers. Never credit state from the event
body alone.
10. Render explicit lifecycle states
| State | Wallet UI |
|---|---|
proposed | User can inspect and cancel before preparation |
prepared | Exact network, targets, value, and expiry are visible |
awaiting_signature | Wallet is the only signing boundary |
submitted / mined_unconfirmed | Pending; balance is not yet credited |
final_success | Reread canonical position and show completion |
| blocked or failed | Show stable reason, request ID, and safe retry guidance |
Test the complete journey
Run node --test examples/developer-platform/earn-program-reference/test/*.test.mjs, then test a
Sepolia deposit and redemption with: wrong origin, wrong chain, expired challenge, denied signature,
failed simulation, replaced transaction, duplicate register request, webhook replay, stale resource,
closed browser, and successful resume from the server inbox.
Production checklist
- Publishable key is restricted to exact hosted origins; no Torq secret is shipped to clients.
- Challenge audience and prepared workflow are validated before every wallet signature request.
- Every catalog field comes from the documented canonical API schema.
- Eligibility and freshness fail closed at proposal and signing time.
- The wallet sees exact target, value, network, action, fees, and expiry.
- Customer balance changes only after workflow and transaction
final_successplus resource reread. - Signed webhooks enter an idempotent durable inbox and trigger reconciliation.
- Deposit, redemption, reload, retry, reorg, and error cases pass on Sepolia.
- Private Preview labels, disclosures, support route, and status link remain visible.
Continue with the API reference, SDK reference, and webhook guide.