> ## Documentation Index
> Fetch the complete documentation index at: https://docs.signalite.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Arc Integration Handoff

> Production handoff checklist for Arc mobile/backend integration.

Use this page when wiring Arc to Signalite production.

## Production endpoints

| Surface                         | URL                                           |
| ------------------------------- | --------------------------------------------- |
| API base                        | `https://portal.signalite.ai`                 |
| OpenAPI                         | `https://portal.signalite.ai/api/v1/openapi`  |
| Product health                  | `GET /api/v1/health`                          |
| Bootstrap screen payload        | `GET /api/v1/bootstrap?wallet=0x...`          |
| Partner deposit action          | `POST /api/v1/partner/actions/deposit`        |
| Partner instant withdraw action | `POST /api/v1/partner/actions/redeem-instant` |
| Partner queued withdraw action  | `POST /api/v1/partner/actions/redeem-async`   |
| Partner claim action            | `POST /api/v1/partner/actions/claim`          |
| Partner action status           | `GET /api/v1/partner/actions/{id}`            |
| Submit tx hash                  | `POST /api/v1/partner/actions/{id}/submit`    |
| Arc webhook receiver            | `https://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.

```ts theme={null}
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.

```ts theme={null}
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:

```txt theme={null}
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:

```bash theme={null}
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:

```txt theme={null}
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:

```ts theme={null}
const config = await signalite.getConfig();
const destination = config.deposit.funding.destination;
const sources = config.deposit.funding.sourceNetworks;
```

Current production destination:

```txt theme={null}
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:

```ts theme={null}
const config = await signalite.getConfig();
const destinations = config.withdraw.supportedDestinations;
```

When Arc creates a withdrawal Partner Action, pass the selected destination:

```ts theme={null}
await signalite.createPartnerRedeemInstantAction({
  idempotencyKey,
  shares: sharesRaw,
  account: userWallet,
  receiver: userWallet,
  slippageBps: 50,
  receiveChainId: 8453,
});
```

Use the same field for queued redeem and later claim:

```ts theme={null}
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:

```txt theme={null}
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:

```bash theme={null}
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.
