Skip to main content
Use this page when wiring Arc to Signalite production.

Production endpoints

SurfaceURL
API basehttps://portal.signalite.ai
OpenAPIhttps://portal.signalite.ai/api/v1/openapi
Product healthGET /api/v1/health
Bootstrap screen payloadGET /api/v1/bootstrap?wallet=0x...
Partner deposit actionPOST /api/v1/partner/actions/deposit
Partner instant withdraw actionPOST /api/v1/partner/actions/redeem-instant
Partner queued withdraw actionPOST /api/v1/partner/actions/redeem-async
Partner claim actionPOST /api/v1/partner/actions/claim
Partner action statusGET /api/v1/partner/actions/{id}
Submit tx hashPOST /api/v1/partner/actions/{id}/submit
Arc webhook receiverhttps://api.getarc.xyz/webhooks/signalite

Server-side secret

Arc receives a Signalite partner API key out of band. Store it only on the Arc backend. Never ship the key in a mobile app, browser app, analytics event, crash log, or client config.
import { createShlpApiClient } from "@arc/shlp-sdk/api";

export const signalite = createShlpApiClient({
  baseUrl: "https://portal.signalite.ai",
  partnerApiKey: process.env.SIGNALITE_PARTNER_API_KEY,
});

Mobile deposit flow

Arc should expose one app-level Deposit CTA. Do not make the user press a separate Approve button and then a separate Deposit button. If allowance is missing, approval is an internal wallet step inside the same guided deposit flow; once reusable allowance exists, later deposits should skip approval. Signalite Partner Actions start only after the user wallet owns HyperEVM USDC. If the user selects USDC on Ethereum, Arbitrum, Base, Optimism, Polygon, Avalanche, Solana, or another Relay-supported source, Arc must first use its Relay/funding flow to deliver USDC to the same user EVM wallet on HyperEVM. Then Arc creates the Signalite Partner Action for the actual received HyperEVM USDC amount.
  1. Arc mobile checks GET /api/v1/config and reads deposit.funding.destination and deposit.funding.sourceNetworks.
  2. If the selected source is not HyperEVM USDC, Arc uses Relay or its own funding rail to send USDC to the user’s HyperEVM EVM wallet.
  3. Arc waits until USDC.balanceOf(userWallet) on HyperEVM is at least the deposit amount Arc will pass to Signalite. Use the actual received amount, not the pre-bridge source amount.
  4. Arc mobile asks Arc backend to create a Signalite deposit action.
  5. Arc backend calls createPartnerDepositAction with a stable idempotencyKey.
  6. Signalite validates product readiness and sponsors HyperEVM HYPE if the user wallet needs gas.
  7. Arc backend returns the ordered wallet steps to mobile.
  8. Mobile signs/sends each step in order.
  9. If the step is an approval, mobile waits for the approval receipt, then verifies USDC.allowance(account, depositPipe) on HyperEVM chain 999 for the same wallet before refreshing into a deposit step.
  10. After the primary deposit tx is sent, mobile waits for a HyperEVM receipt and verifies it is the successful deposit tx.
  11. Arc backend calls submitPartnerAction only with the verified deposit tx hash.
  12. Arc polls getPartnerAction and consumes signed Signalite webhooks.
const action = await signalite.createPartnerDepositAction({
  idempotencyKey: `arc:${userId}:deposit:${clientRequestId}`,
  amount: 1_000_000n,
  account: userWallet,
  receiver: userWallet,
  slippageBps: 50,
});
If a step has refreshQuoteAfterConfirm=true, recreate the action/quote only after that tx confirms and the Signalite vault allowance is at least the deposit amount. Do not ask the wallet to simulate/sign deposit while allowance is zero or stale. Critical submit rule:
  • Do not submit an approval tx hash.
  • Do not submit a Relay/funding tx hash.
  • Do not submit a wallet placeholder hash.
  • Do not submit any hash that eth_getTransactionReceipt cannot find on HyperEVM chain 999.
  • For deposits, the submitted hash must have a successful receipt and transaction target must equal the Signalite V2 depositPipe returned in Partner Action metadata and /api/v1/config.
  • If the refreshed Partner Action still contains an approval step, stop and show an approval/allowance pending state. Do not skip that approval and do not execute deposit from a stale action.
Arc’s GET /api/earn/signalite/actions/:id route must use Cache-Control: no-store and must not return conditional 304 responses. Polling needs a fresh JSON body with the current action status and step statuses.

Gas model

Production uses server-side gas sponsorship inside authenticated Partner Actions. faucetEnabled=false on /api/v1/gas/status is expected. Arc should not ask normal deposit users to source HYPE manually before using the Partner Action flow. If gas sponsorship is disabled, unfunded, rate-limited, or unable to reach the wallet, Signalite fails action creation closed instead of returning wallet steps that cannot execute.

Webhook setup

Arc’s production receiver is deployed at:
https://api.getarc.xyz/webhooks/signalite
The receiver verifies the raw request body with the Signalite HMAC headers and stores a short Redis audit marker for the most recent delivery. The signing secret is stored only in the Arc backend as SIGNALITE_WEBHOOK_SIGNING_SECRET. The Signalite partner webhook is currently configured for:
  • partner_action.created
  • partner_action.submitted
To inspect or re-apply the configuration:
PARTNER_API_KEY=shlp_... \
PARTNER_WEBHOOK_URL=https://api.getarc.xyz/webhooks/signalite \
PARTNER_WEBHOOK_APPLY=1 \
API_BASE=https://portal.signalite.ai \
pnpm configure:partner-webhook
Use PARTNER_WEBHOOK_ROTATE_SECRET=1 only when rotating the secret, then immediately update SIGNALITE_WEBHOOK_SIGNING_SECRET on the Arc backend before enabling delivery again. Signalite returns the signing secret only on create/rotate. Verify the raw request body with verifySignaliteWebhook from @arc/shlp-sdk/webhooks.

Product data Arc can show

Arc can use the bootstrap payload or SDK helpers to display:
  • current yield after fees
  • TVL
  • instant liquidity / idle USDC
  • HLP allocation and proof buckets
  • projected growth
  • APY/APR over time
  • user USDC, sHLP, allowance, activity, and redeem state

Cross-chain USDC funding contract

Arc users deposit USDC from many networks, but Signalite receives only HyperEVM USDC. The integration boundary is:
Arc/Relay: source USDC -> HyperEVM USDC in user's EVM wallet
Signalite: HyperEVM USDC -> sHLP through Partner Actions
Use GET /api/v1/config as the source of truth:
const config = await signalite.getConfig();
const destination = config.deposit.funding.destination;
const sources = config.deposit.funding.sourceNetworks;
Current production destination:
chainId: 999
chainName: HyperEVM
token: USDC 0xb88339CB7199b77E23DB6E890353E22632Ba630f
Supported first-class source examples include Ethereum, Arbitrum, Base, Optimism, Polygon, Avalanche, and Solana USDC. For EVM sources, Arc can use a Viem Relay wallet adapter. For Solana, use Relay’s SVM/Solana wallet adapter and settle the Relay output to the user’s associated EVM wallet on HyperEVM. Do not call submitPartnerAction with Relay transaction hashes. The submitted hash must be the final Signalite V2 pipe transaction on HyperEVM.

Cross-chain withdrawal receive contract

Signalite withdraw and claim wallet transactions always settle USDC on HyperEVM first. Arc can let a user choose a final receive chain, but that choice is represented by receiveChainId plus a post-settlement bridge, not by a different Signalite vault claim. Read supported destinations from:
const config = await signalite.getConfig();
const destinations = config.withdraw.supportedDestinations;
When Arc creates a withdrawal Partner Action, pass the selected destination:
await signalite.createPartnerRedeemInstantAction({
  idempotencyKey,
  shares: sharesRaw,
  account: userWallet,
  receiver: userWallet,
  slippageBps: 50,
  receiveChainId: 8453,
});
Use the same field for queued redeem and later claim:
await signalite.createPartnerRedeemAsyncAction({
  idempotencyKey,
  shares: sharesRaw,
  account: userWallet,
  slippageBps: 50,
  receiveChainId: 8453,
});

await signalite.createPartnerClaimAction({
  idempotencyKey,
  id: redeemId,
  account: userWallet,
  receiver: userWallet,
  receiveChainId: 8453,
});
For a non-HyperEVM destination, action.metadata.withdrawal tells Arc:
  • the user’s requested destination
  • the HyperEVM settlement receiver
  • whether a Relay post-settlement bridge is required
  • the amount source to bridge after the HyperEVM tx confirms
Arc must persist the selected destination for queued redeems and pass the same receiveChainId again at claim time. Do not submit Relay bridge hashes, destination-chain hashes, or cross-chain funding hashes to Signalite. Queued withdraws are FIFO. If an older 20 USDC queued request exists and the buffer currently has 6 USDC, a later 2 USDC request remains pending until the older request can be fulfilled or cancelled. Do not build Arc UI or keeper logic that skips the queue.

GetArc agent prompt

Use this copy-paste prompt when handing the implementation to a GetArc coding agent:
You are working in the GetArc codebase. Make the Signalite sHLP Earn integration
work in production.

Do not redesign Earn. Do not rebuild the old direct Signalite quote flow.
Fix the existing mobile/backend integration so a real TestFlight user can
deposit into Signalite sHLP.

Read the Signalite Mintlify docs first:
- mintlify/operations/arc-integration-handoff.mdx
- docs/partner-integration-contract.md
- docs/partner-backend-contract.md
- docs/partner-mobile-quickstart.md

The Mintlify docs exist so partners do not guess the integration from chat or
screenshots. Treat them as the partner-facing contract. If code and docs
disagree, use the documented production contract, then update docs in the same
PR if the contract really changed.

After the GetArc fix, update the Mintlify handoff page so the next partner or
agent can integrate without reading chat history. The docs must clearly state:
- production API base
- required backend-only env vars
- mobile-safe routes
- Partner Action deposit sequence
- webhook URL and signature verification
- APY display rule
- verification commands and definition of done

Current production symptoms:
- Signalite sheet opens, but tapping Deposit does not reach the backend.
- Railway logs do not show POST /api/earn/signalite/actions/deposit.
- Mobile is still blocking locally with:
  "Your wallet needs at least 0.001 HYPE on HyperEVM for gas.
   Signalite gas top-up is not available for this wallet right now."
- That local HYPE preflight is wrong for production Partner Actions.
- GET https://api.getarc.xyz/api/earn/vaults has returned {"vaults":[]}; do not
  ship that because it breaks Steakhouse/Gauntlet display.

Production config:
- Signalite API: https://portal.signalite.ai
- GetArc API: https://api.getarc.xyz
- Signalite webhook receiver: https://api.getarc.xyz/webhooks/signalite
- HyperEVM chainId: 999
- HyperEVM USDC: 0xb88339CB7199b77E23DB6E890353E22632Ba630f
- Signalite V2 share/core: 0xe9fa353a39b38E8106B1D2704eFF6765F12345f8
- Signalite V2 deposit pipe: 0xDFB13AF07CE5A5C968Ab18AC28fEB90E9FC00e06
- Signalite V2 redemption pipe: 0x28E0FA93F213265e4ceb32785b806a7f91890b30

Backend-only env vars:
- SIGNALITE_PARTNER_API_KEY
- SIGNALITE_WEBHOOK_SIGNING_SECRET
- SIGNALITE_API_BASE=https://portal.signalite.ai

Never put SIGNALITE_PARTNER_API_KEY or SIGNALITE_WEBHOOK_SIGNING_SECRET in Expo,
EAS, app.json, mobile .env, frontend code, logs, Sentry extras, or analytics.

Mobile files:
app/earn-deposit.tsx
app/earn-withdraw.tsx
app/earn/[vaultId].tsx
app/(app)/earn.tsx
src/services/signalite.ts
src/hooks/useSignalite.ts
src/hooks/useEarnVaults.ts
src/services/api.ts
src/constants/earnVaults.ts

Backend files:
src/routes/signalite.ts
src/services/signalite-partner.ts
src/routes/webhooks.ts
src/routes/earn.ts
src/config/env.ts

Required Signalite backend routes:
GET  /api/earn/signalite/bootstrap?wallet=0x...
GET  /api/earn/signalite/partner/status
POST /api/earn/signalite/actions/deposit
POST /api/earn/signalite/actions/redeem-instant
POST /api/earn/signalite/actions/redeem-async
POST /api/earn/signalite/actions/claim
POST /api/earn/signalite/actions/cancel
GET  /api/earn/signalite/actions/:id
POST /api/earn/signalite/actions/:id/submit
POST /webhooks/signalite
GET  /webhooks/health

Backend requirements:
- POST /api/earn/signalite/actions/deposit requires mobile auth.
- account must match the authenticated user's EVM wallet.
- receiver must match the authenticated user's EVM wallet.
- backend resolves the authenticated user's associated EVM/HyperEVM wallet; it
  must not trust arbitrary mobile-supplied wallet addresses.
- EVM address comparisons are case-insensitive. Mixed-case/checksum and
  lowercase forms of the same address are the same wallet.
- normalize addresses to lowercase for cache keys, idempotency keys, Redis keys,
  and database lookup keys.
- idempotency key is created server-side.
- backend calls Signalite POST /api/v1/partner/actions/deposit using
  SIGNALITE_PARTNER_API_KEY.
- mobile must not choose arbitrary Signalite endpoints.

Correct mobile deposit flow:
1. User selects Signalite sHLP and enters amount.
2. If selected funding asset is not HyperEVM USDC, use GetArc/Relay funding
   first so the user's EVM wallet receives HyperEVM USDC. For Solana, use the
   Relay SVM/Solana wallet adapter and set the Relay output recipient to the
   user's associated EVM wallet on HyperEVM.
3. Call GetArc backend POST /api/earn/signalite/actions/deposit with:
   clientRequestId, refreshOrdinal, account, receiver: account, amountUsdc,
   slippageBps: 50.
4. Sign only the ordered steps returned in action.steps.
5. If the current step is an approval and refreshQuoteAfterConfirm=true, wait
   for approval receipt, then verify `USDC.allowance(account, depositPipe)` on
   HyperEVM chain 999 using the same wallet that signed approval. Continue only
   when allowance is at least the deposit amount.
6. Increment refreshOrdinal and create a fresh Partner Action before signing the
   deposit step.
7. If the refreshed Partner Action still contains an approval step, stop and
   show approval/allowance pending. Do not skip that approval.
8. When the deposit tx is sent, wait for its HyperEVM receipt. It must exist on
   chain 999, receipt.status must be success, and the transaction target must
   be the `depositPipe` returned by Signalite Partner Action metadata.
9. Submit only that verified deposit tx hash to:
   POST /api/earn/signalite/actions/:id/submit.
10. Poll GET /api/earn/signalite/actions/:id until terminal. This route must
   return fresh JSON, not 304.

Correct withdrawal lifecycle:
1. Use GET /api/earn/signalite/bootstrap?wallet=0x... as source of truth for
   user sHLP balance, instant liquidity, pending redeems, and claimable redeems.
2. If instant liquidity covers the withdrawal, create:
   POST /api/earn/signalite/actions/redeem-instant.
3. If instant liquidity does not cover it, create:
   POST /api/earn/signalite/actions/redeem-async.
4. Sign only ordered action.steps.
5. Before opening the wallet, run `eth_call` against the exact withdraw/redeem
   tx at latest HyperEVM state. If it reverts, do not show the wallet request;
   re-route to queued withdrawal or show a blocking state.
6. Wait for final withdraw/redeem receipt on HyperEVM, verify success and
   Signalite V2 pipe target, then submit the tx hash.
7. Poll action status and bootstrap.
8. If async redeem is pending, show pending state. This means funds are waiting
   for keeper/HLP liquidity and is not an app error.
9. When bootstrap marks the redeem claimable, create:
   POST /api/earn/signalite/actions/claim.
10. If a pending redeem is cancellable, expose cancel through:
   POST /api/earn/signalite/actions/cancel.

Withdrawal UX rules:
- Do not hide pending redeems.
- Do not show pending HLP unwind as failed.
- Do not promise instant withdrawal when the instant buffer is insufficient.
- Show claimable separately from pending.
- Webhooks are hints; continue polling bootstrap/redeems/action status.

Delete or bypass this old Signalite deposit logic:
- quoteSignaliteDeposit(...)
- ensureSignaliteGas(...)
- local HyperEVM HYPE balance preflight blocker
- "Add HYPE & deposit" button copy
- "Your wallet needs at least 0.001 HYPE..." before Partner Action creation

The app may display a backend-returned gas/action error after Partner Action
creation fails, but it must not block before calling:
POST /api/earn/signalite/actions/deposit.

APY rule:
- GetArc UI must say APY, not APR.
- Remove visible APR copy from Earn list, Earn detail, and deposit flow.
- Map Signalite yield to the app's APY field from `hlp.vault.apyPct`.
- `hlp.vault.apyPct` and `hlp.vault.ratePct` are the Signalite display APY:
  trailing 1y HLP performance net of Signalite performance fees on positive
  returns. This is the same APY shown in the Signalite web app.
- Do not use `hlp.signaliteProjected.projectedAprPct` or
  `hlp.returns.windows["365d"].apyPct` for the product card unless Signalite
  explicitly asks you to show raw HLP or current APR context.

Steakhouse/Gauntlet rule:
- Do not return {"vaults":[]} in production.
- If Privy Earn vault IDs return 404 Vault not found, keep GET /api/earn/vaults
  working for display using public Morpho fallback data for Gauntlet/Steakhouse
  APY and TVL.
- Fallback display data is not proof that Privy managed deposits work. If Privy
  IDs are stale, call out that current Privy Dashboard vault IDs are required
  for managed Privy deposits.

Webhook rule:
- Signalite webhook remains https://api.getarc.xyz/webhooks/signalite.
- GET /webhooks/health must show signaliteSigningSecretConfigured=true.

Verification:
Run npm run typecheck in GetArc API and mobile.
Run npm test in GetArc API if available.
curl -sS https://api.getarc.xyz/api/earn/vaults
curl -sS https://api.getarc.xyz/webhooks/health
Tail Railway logs while testing TestFlight:
railway logs --service arc --since 10m --lines 400 --json

Definition of done:
1. Deposit tap reaches POST /api/earn/signalite/actions/deposit.
2. Mobile no longer blocks Signalite deposit with the old local 0.001 HYPE
   error before creating a Partner Action.
3. Backend returns a Signalite Partner Action with ordered steps.
4. Approval step refreshes the Partner Action before deposit when required.
5. Deposit tx hash is verified on HyperEVM and submitted through
   POST /api/earn/signalite/actions/:id/submit.
6. Polling works through GET /api/earn/signalite/actions/:id.
7. /webhooks/signalite verifies Signalite signatures.
8. Withdrawal UI handles instant, queued pending, claimable, claimed, and
   cancellable redeem states.
9. /api/earn/vaults returns visible Steakhouse/Gauntlet rows, not an empty array.
10. Signalite UI says APY, not APR.
11. Mobile bundle contains no Signalite partner key or webhook secret.

If POST /api/earn/signalite/actions/deposit is absent after tapping Deposit,
the bug is still in mobile before the backend call.

Release checks

Before enabling production traffic, Signalite should show:
API_BASE=https://portal.signalite.ai npm run smoke:api
API_BASE=https://portal.signalite.ai npm run readiness:partner
PARTNER_API_KEY=shlp_... API_BASE=https://portal.signalite.ai pnpm readiness:partner-prod
PARTNER_API_KEY=shlp_... PARTNER_READY_STRICT=1 API_BASE=https://portal.signalite.ai pnpm readiness:partner-prod
PARTNER_API_KEY=shlp_... PARTNER_SIMULATE_AUTH=1 PARTNER_REQUIRE_AUTH_ACTION=1 API_BASE=https://portal.signalite.ai pnpm simulate:partner-mobile
Strict readiness is the launch gate. It must return verdict=ready, zero warnings, and zero blockers before Arc enables production traffic.