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.