# Hood Long frontend integration guide for coding agents Last reviewed: 2026-08-05 Production origin: https://hoodlong.com Trading API: https://hoodlong.com/functions/v1/trade-api OpenAPI: https://hoodlong.com/openapi.json Browser-agent tools: https://hoodlong.com/webmcp-tools.json Human documentation: https://hoodlong.com/docs/api/ Support: https://t.me/robinhoodlong ## Purpose Use this document when building a new Hood Long frontend. It describes the implemented production interface and the safety and state-management requirements a compatible frontend must preserve. The frontend is non-custodial. A coding agent may build market discovery, quoting, position views, lending views, and review screens. The user must explicitly approve every wallet signature and transaction. Never ask for or store a private key. ## Sources of truth 1. Treat the `markets` API response as the runtime source for network, market, route, risk-limit, pause, and contract availability. 2. Treat Robinhood Chain as authoritative for transaction and loan state. 3. Treat `positions` and `activity` as indexed views that may briefly lag the chain. 4. Treat `/openapi.json` as the request schema. If this guide and the schema differ, stop and report the mismatch instead of guessing. 5. Never copy private RPC, 0x, Rialto, Birdeye, keeper, or admin credentials into browser code. Hood Long performs price and swap-provider calls on the server. ## Network - Name: Robinhood Chain - Chain ID: 4663 decimal, 0x1237 hexadecimal - Native gas and trade-value asset: ETH - Public RPC: https://rpc.mainnet.chain.robinhood.com - Explorer: https://robinscan.io - WETH: 0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73 - BorrowerOperations: 0xC8318ee5d8664eb1d625Bc0079B54AF1C8eFAAbd - TokenHolder: 0x05E046315D76D5DEbF3bE75bDD318CB0c55cB9Cb - ETH lending pool / hETH: 0xe10Cc08AA8F1481AaddA1fb774ff1d1a2e54784F Suggested `wallet_addEthereumChain` data: ```json { "chainId": "0x1237", "chainName": "Robinhood Chain", "nativeCurrency": { "name": "Ether", "symbol": "ETH", "decimals": 18 }, "rpcUrls": ["https://rpc.mainnet.chain.robinhood.com"], "blockExplorerUrls": ["https://robinscan.io"] } ``` Before each signature or transaction, verify all of the following: - the provider's current account equals the address shown in the UI; - the wallet is on chain 4663; - the wallet can cover the transaction `value` plus a buffered network fee; - the preparation is still unexpired; - the user has seen and explicitly accepted a visible review. Never render a cached address as connected without re-reading the provider account. On account change, clear quotes, preparations, pending confirmations, balances, and position data before loading the new wallet. On disconnect, revoke/remove the wallet session where supported and clear all local connection state. A stale wallet identity is a transaction-integrity bug. ## API conventions The API accepts POST JSON at one endpoint. Put the operation name in `action`. ```js const API = "https://hoodlong.com/functions/v1/trade-api"; export async function hoodLong(action, input = {}, signal) { const response = await fetch(API, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ action, ...input }), signal }); const data = await response.json(); if (!response.ok || !data.ok) { const error = new Error(data.error || `Hood Long request failed (${response.status})`); error.status = response.status; error.details = data.details ?? null; throw error; } return data; } ``` Use decimal strings for ETH, WETH, leverage, and user-entered token amounts. Do not use JavaScript floating point for wei, token base units, transaction values, route amounts, shares, debt, or fees. Returned blockchain quantities are strings. Prepared transaction fields use EIP-1193 hexadecimal quantities. There is no browser API key. Live trade preparation is authorized by a short-lived personal wallet signature. CORS is enabled for public integration. Do not interpret this as permission to automate user transactions. ## Recommended frontend modules - API client: action requests, response validation, cancellation, and normalized errors. - Wallet adapter: connect/disconnect, account and chain events, message signing, transaction submission, and uncertain-broadcast recovery. - Market store: markets, compact prices, selected market, liquidity, and chart pages. - Trade store: ticket, affordability, indicative quote, review snapshot, live preparation, submission, and recording. - Position store: paginated presentation, fast post-submit reconciliation, background refresh, and terminal states. - Earn store: pool state, lender state, hETH decimals, deposit preview, queue request, and cancellation. - UI state: app loaders, surface skeletons, button progress, toasts, styled confirmations, empty states, and retry controls. Do not place wallet side effects inside rendering code. Make signing and transaction submission callable only from an immediate, explicit user event. ## Application boot state machine Use explicit states instead of a blank interface: `booting -> loading_markets -> wallet_disconnected | restoring_wallet -> ready | recoverable_error` 1. Show an app-level loader. 2. Fetch `markets`. 3. Render markets and network even if no wallet is connected. 4. Query the wallet provider for its actual selected account. Never trust an address from local storage. 5. If a proven session exists, verify/switch the chain and load positions plus pool state. 6. Restore any locally stored transaction hash that was broadcast but not recorded. 7. Remove the app loader only after the market list and primary route are usable. Recommended refresh cadence: - `market_prices`: every 30 seconds while visible. - `positions`: every 15 seconds while a wallet is connected and the page is visible. - `markets`: every 5 minutes, and after a route/pause error. - after an open or close submission: call `transaction_status` and refresh positions about every 750 ms for up to 120 seconds, then return to normal cadence. - chart history: load the newest page, then request older pages with `before` only when the user scrolls left. Cancel stale requests when the selected market or wallet changes. Discard any response whose request identity no longer matches current state. ## Market discovery and availability Call: ```json { "action": "markets" } ``` Only display a market as tradable when it is present in the response. For a long trade, require: - market `listingStatus` is active or limited; - market `borrowingPaused` is false; - `routes.long.quotable` is true for quote controls; - `routes.long.executable` is true before entering confirmation; - the collateral is within `limits.minCollateralWeth` and `limits.maxCollateralWeth`; - market/pool liquidity supports the requested size; - the wallet can cover collateral plus gas. Do not hardcode a list of markets. Stock-token markets may appear only when their oracle and executable route are available. Short is currently unavailable until a separate ShortOperations adapter is deployed. Hide or clearly disable unavailable directions based on `routes`, not product copy. Use `market_liquidity` immediately before determining the maximum trade size: ```json { "action": "market_liquidity", "marketId": "" } ``` Differentiate in the UI: - DEX liquidity: how much the external venue can execute; - available pool liquidity: how much WETH can be borrowed now; - remaining market exposure: the market's on-chain exposure headroom; - maximum collateral: user collateral, not maximum borrow. ## Price history Request: ```json { "action": "price_history", "marketId": "", "interval": "1h", "before": "2026-08-05T00:00:00.000Z" } ``` Supported intervals are `continuous`, `10m`, `30m`, `1h`, and `4h`. `continuous` returns line points. Other intervals return candles. Prices are quoted in ETH. Use `nextBefore` while `hasMore` is true for rolling history. Charts must support horizontal pan/scroll, pinch or wheel zoom, touch crosshair, a visible snapped point/candle, time and value axes, and position/liquidation overlays when position data is available. Market capitalization is displayed in USD. Position, entry, current value, PnL, fees, and liquidation values are displayed in ETH to keep the user's trade accounting internally consistent. ## Trade ticket and affordability The ticket must show: - selected token and verified contract address; - direction; - collateral in ETH; - leverage; - estimated position size in ETH; - current entry and liquidation levels in ETH per token; - borrow APR and accrued-interest implications; - DEX liquidity, pool liquidity, and exposure headroom; - 1% opening fee estimate; - gas as a separate wallet expense; - a disabled review button until all required inputs and funds are valid. Before enabling review, estimate gas against the candidate transaction when available. Otherwise use a conservative opening fallback gas limit and current fee data. Require: `wallet ETH balance >= transaction value + buffered gas estimate` Do not let the wallet discover predictable insufficient-funds failures. Show the maximum affordable collateral at the input. Recheck funds against the final prepared transaction immediately before broadcast. ## Indicative quote ```json { "action": "quote", "wallet": "0x...", "marketId": "", "direction": "long", "collateral": "0.01", "leverage": "2" } ``` Quotes are indicative and expire. Display their expiry and never treat a quote response as an executable transaction. Debounce quote requests. Do not poll executable 0x or Rialto routes; the server requests those only during user-authorized preparation. ## Live open state machine Use these UI states: `editing -> quoting -> review_ready -> authorizing_preparation -> preparing_route -> final_review -> wallet_pending -> broadcast_unknown | broadcast -> recording -> submitted -> mined -> confirmed | failed` Never collapse these into one button with no feedback. Every asynchronous state requires a spinner or status label, and the final action must be disabled while active. ### 1. Request a preparation challenge Send the exact intended trade: ```json { "action": "prepare_challenge", "wallet": "0x...", "marketId": "", "direction": "long", "collateral": "0.01", "leverage": "2", "walletClientCapability": "native-value-v2" } ``` The response contains `challenge.id`, `challenge.message`, and `challenge.expiresAt`. The challenge lasts approximately two minutes. ### 2. Ask the wallet to sign the exact message With ethers v6: ```js const signature = await signer.signMessage(challenge.message); ``` With EIP-1193, hex-encode the UTF-8 message and use `personal_sign` with `[hexMessage, walletAddress]`. Never send an empty message. Display a human explanation before opening the wallet. ### 3. Prepare the executable route Send the same intent plus: ```json { "action": "prepare", "challengeId": "", "challengeExpiresAt": "", "signature": "0x..." } ``` The response includes `quote` and `preparation`. Continue only when all are true: - `preparation.ready === true`; - `preparation.preparationId` is a UUID; - `preparation.transaction` exists; - `preparation.expiresAt` is in the future; - transaction `from` equals the connected wallet; - transaction `to` equals `preparation.contract` and the documented BorrowerOperations address; - the user can cover `transaction.value` plus buffered gas. Open preparations last approximately 45 seconds. The API may optimistically submit a manual price update before simulating the user transaction. If it returns not ready and asks for review shortly, request a completely new challenge and preparation after a brief user-visible wait; do not reuse the old authorization. The final review must show the current quote, route minimum received, slippage, opening fee, debt, liquidation level, separate gas estimate, transaction value, and countdown/expiry. The user must press a visible final confirmation button before the wallet transaction request. ### 4. Broadcast from the user wallet Send `preparation.transaction` without changing `to`, `data`, or `value`. Preserve the exact preparation ID and trade intent locally before opening the wallet. If the wallet rejects with code 4001, return to review and do not record or retry. If the provider throws but the account nonce increased or the wallet may have returned a hidden transaction hash, enter `broadcast_unknown`. Tell the user not to retry. Search recent/pending blocks or the wallet response for the transaction by sender and nonce. Only resume recording when the hash is known. After a hash is known, verify the chain transaction value equals the reviewed `transaction.value`. A zero-value or mismatched-value transaction cannot represent the reviewed opening. ### 5. Record the broadcast ```json { "action": "record", "wallet": "0x...", "marketId": "", "preparationId": "", "txHash": "0x..." } ``` Recording validates the chain transaction against the reviewed preparation. It is idempotent by transaction hash. If the chain RPC has not seen a just-broadcast hash, retry the same recording values with bounded backoff. Do not broadcast a second transaction. ## Live close state machine Use: `open -> close_review -> authorizing_close -> preparing_close -> final_close_review -> wallet_pending -> broadcast_unknown | broadcast -> recording_close -> closing -> confirmed | failed` 1. Load `positions` for the current wallet and use an open position UUID. 2. Call `prepare_challenge` with `wallet`, `positionId`, and `walletClientCapability: "native-value-v2"`. 3. Sign the exact returned message. 4. Call `prepare_close` with the same wallet and position ID plus challenge fields and signature. 5. Require `preparation.ready` and an unexpired transaction. Close preparations last approximately 20 seconds. 6. Show debt, accrued interest, estimated close fee, sell amount, minimum WETH received, and `topUpWeth`. 7. If `transaction.value` is nonzero, label it as an ETH repayment top-up, not collateral or gas. Gas remains separate. 8. After explicit user confirmation, broadcast the unmodified transaction. 9. Call `record_close` with wallet, position ID, preparation ID, and hash. The server computes a debt-accrual buffer and an automatic top-up when route proceeds may not cover debt and fees. Do not override that calculation. Preparations requiring more than the 1 ETH top-up safety limit are rejected. ## Positions and reconciliation Request: ```json { "action": "positions", "wallet": "0x..." } ``` Show the difference between pending, open, closing, liquidating, closed, liquidated, and failed. Never render a missing current LTV, PnL, or interest value as zero. Use `—`, `Calculating`, or `Awaiting backfill` until the API supplies an authoritative value. For open positions show at minimum: - symbol and token logo; - direction and status; - collateral and position size in ETH; - entry/current/liquidation price in ETH per token; - current LTV and liquidation LTV; - accrued interest in ETH; - PnL in ETH and percent; - transaction confirmations as small secondary text, for example `2/3 confirmations secured`; - Robinscan links for addresses and transactions. Paginate rendering if a wallet has many positions. Keep the canonical array in memory and page the presentation; do not lose fast-updated items when changing pages. Reconcile with: ```json { "action": "transaction_status", "wallet": "0x...", "positionId": "" } ``` or use `txHash`. Terminal failure states include reverted, dropped, replaced, reorged, and invalid. A transaction is fully secured when confirmations are at least `network.requiredConfirmations` from `markets`. If a wallet replaces a transaction: ```json { "action": "record_replacement", "wallet": "0x...", "txHash": "", "replacementHash": "" } ``` Do not create a second position for a replacement. ## Lending / Earn frontend Read pool and wallet state through: ```json { "action": "pool", "wallet": "0x..." } ``` The response supplies the pool address, WETH asset, share decimals, deposits pause, totals, liquidity, utilization, reserve, queue, lender balances, deposit/withdrawal share prices, and performance history. Use the response values instead of recomputing accounting metrics. Important UX: - hETH is queue-only for withdrawal, not instant ERC-4626 redemption. - Deposit price and withdrawal price are separate on-chain accounting views. - Lender APY is variable and based on realized lender interest, not merely accrued or uncollected interest. - A queued withdrawal remains exposed to pool gains and losses until processed. - The normal keeper can process after 24 hours; public processing is available after 72 hours. - Show first-loss reserve, outstanding principal, available liquidity, utilization, queued assets/shares, current lender rate, completed-day realized APY, partial-day yield, lifetime realized lender yield, protocol fee share, and loss history. Pool ABI fragments required by a user frontend: ```text function previewDeposit(uint256 assets) view returns (uint256) function previewRedeem(uint256 shares) view returns (uint256) function depositETH(address receiver, uint256 minimumShares) payable returns (uint256) function deposit(uint256 assets, address receiver) returns (uint256) function requestRedeem(uint256 shares, address receiver) returns (uint256) function cancelWithdrawal(uint256 requestId) ``` WETH ABI fragments: ```text function allowance(address owner, address spender) view returns (uint256) function approve(address spender, uint256 amount) returns (bool) function balanceOf(address owner) view returns (uint256) ``` ETH deposit: 1. Parse the amount to 18-decimal wei. 2. Preview shares. 3. Set `minimumShares` to no less than 99.5% of the preview, unless product policy changes. 4. Check ETH amount plus buffered gas. 5. Show a styled review. 6. Call `depositETH(wallet, minimumShares, { value: assets })`. WETH deposit: 1. Check WETH balance and ETH gas balance. 2. Check allowance. 3. If insufficient, request a WETH approval and wait for one confirmation. 4. Recheck account, chain, balances, and allowance. 5. Call `deposit(assets, wallet)`. Withdrawal: 1. Parse hETH using `pool.shareDecimals`; do not assume 18 decimals. 2. Block amounts above owned shares. 3. Show the current withdrawal-price estimate and queue timing. 4. Call `requestRedeem(shares, wallet)` after explicit wallet confirmation. 5. Allow cancellation only for an active request owned by the wallet. ## Browser-agent / WebMCP support If the new frontend supports WebMCP, expose equivalent tools to `/webmcp-tools.json`. Preserve these invariants: - read tools may inspect state without side effects; - form tools may select a market, fill inputs, navigate, and report affordability; - review tools may open visible confirmation cards; - no tool may press confirm, sign, approve, send, or submit; - multiple close requests appear as a visible review queue, not a batch signature; - cap agent-prepared close reviews at 20 positions per call; - tool outputs must say whether user confirmation is required and whether anything was submitted. ## Loading and error behavior Every asynchronous user path must expose progress at the correct scope: - whole-app loader during boot; - skeletons for market, position, chart, and pool data; - button spinner and changing label for direct actions; - inline input validation for collateral, WETH, hETH, leverage, limits, and funds; - non-blocking background refresh indicator; - styled modal confirmations and styled error notices; never use `window.alert`, `confirm`, or `prompt`; - an explicit empty state when there are no positions or queue items; - a retry action only when retrying cannot duplicate a transaction. Do not show raw RPC errors to users. Map common cases: - user rejected: `The wallet request was cancelled.` - insufficient funds: show required collateral/top-up, separate estimated gas, and maximum affordable amount; - chain not added: call `wallet_addEthereumChain`, then switch; - stale preparation: close review, refresh state, and ask the user to review again; - borrowing paused or route unavailable: disable the market/action and refresh markets; - broadcast unknown: `Your wallet may have submitted this transaction. Do not retry while Hood Long checks the chain.` - recording unavailable after known broadcast: preserve the hash and retry recording only; ## API errors and retry policy Errors are JSON: ```json { "ok": false, "error": "Human-readable message", "details": null } ``` - 400: invalid, stale, expired, or malformed input. Correct or re-read state. No automatic loop. - 403: feature or live path disabled. Keep it unavailable. - 404: market, position, or transaction not found. Refresh canonical data. - 409: state conflict such as outdated wallet capability, pause, mismatch, or already-closed loan. Re-read before any retry. - 429 or 5xx on read-only actions: bounded retry with jitter is acceptable. - after a known transaction hash, `record` and `record_close` may be retried with the same exact payload because they are idempotent. - never automatically repeat `prepare_challenge`, wallet signing, wallet broadcast, deposit, requestRedeem, or cancelWithdrawal. Preparation authorization is limited to 12 successful preparations per wallet per minute. Debounce UI actions and disable buttons while active. ## Required routes and responsive layout At minimum provide: - `/app/:base/:quote` or an equivalent token permalink, for example `/app/CASHCAT/ETH`; - trade; - positions; - earn; - docs/support links. Desktop should support a collapsible, sufficiently wide market sidebar; a resizable chart/agent split; collapsible agent; and a fixed, readable order ticket. Mid-size screens must not let the sidebar cover controls. Mobile should use full-width panels, bottom-safe spacing, touch chart controls, dismissible wallet UI, and review sheets that scroll independently without locking the page after closing. Token rows should include a logo, ticker, `MC` column label, USD market cap, address copy link, and status chips that do not clip. Use discovered token logos with a safe symbol/mark fallback. ## Acceptance tests before release A coding agent must document the result of each test. Use a staging wallet first and small real-value transactions only with explicit owner authorization. 1. Load markets and charts with no wallet; agent chat/read-only data must not return a 400 because no wallet is connected. 2. Connect an injected wallet to the wrong chain; add Robinhood Chain and switch successfully. 3. Connect through WalletConnect; verify the preparation signature message is visible and non-empty. 4. Change accounts, disconnect, hard refresh, and reconnect a different account; no old address, positions, quote, or allowance may remain. 5. Enter collateral above wallet funds, pool liquidity, market maximum, and exposure headroom; each must be blocked at input/review with a specific maximum. 6. Open a small long; verify transaction `value` equals collateral, gas is separate, only one position is created, and confirmation count progresses quickly. 7. Simulate an API recording failure after a known broadcast; the UI must preserve the hash, avoid a second broadcast, and recover idempotently. 8. Reject signing and reject transaction submission; the UI returns to a safe review state with no phantom position. 9. Close an open position; verify any ETH top-up is labeled separately, position progresses through closing, and realized PnL/fees appear after indexing. 10. Replace or speed up a pending transaction; verify `record_replacement` links it and does not duplicate a position. 11. Render more positions than one page; verify pagination, refresh, and fast-lane updates preserve the current page and newly updated records. 12. Deposit native ETH, deposit WETH with approval, request withdrawal, and cancel a request; validate balances, gas prevention, hETH decimals, queue timing, and loaders. 13. Test all charts with mouse, wheel, keyboard where applicable, and touch; verify pan, zoom, snapped marker, axes, rolling history, and liquidation overlays. 14. Test API 400, 403, 404, 409, 429, and 5xx states; no raw provider error, silent failure, duplicate submission, or endless spinner is acceptable. 15. Test narrow phone, mid-size tablet, laptop, and wide desktop layouts; no clipped chips, covered controls, horizontal page overflow, trapped scroll, or undismissable modal. ## Release checklist - Generate types from `/openapi.json` or validate every response at runtime. - Keep secrets server-side. - Confirm canonical chain/account before every wallet side effect. - Preserve the human review boundary. - Preserve transaction hashes before and after broadcast. - Validate funds including gas at the input and again before submission. - Hide unavailable markets/directions based on runtime data. - Use ETH denomination for trade accounting and USD only for market cap. - Show unknown/backfilling values as unknown, never zero. - Add telemetry for API action, status, duration, wallet type, state transition, and anonymized error category. Never log signatures, calldata, full wallet identity, or secrets. - Run the acceptance suite on staging, then perform an owner-approved production promotion and production smoke test. ## What a coding agent should return When asked to build a Hood Long frontend, return: 1. the implementation; 2. the generated or hand-written typed API client; 3. a short architecture note naming stores and state machines; 4. the wallet-session and uncertain-broadcast recovery design; 5. the completed acceptance-test matrix with evidence; 6. remaining unsupported product capabilities, especially short execution; 7. deployment status and the exact environment tested. Do not claim a live trade path works based only on mocks, static rendering, or a quote response.