siwe— the one-time Sign-In-With-Ethereum login. The walletpersonal_signs a backend-issued ERC-4361 challenge.session— client-only setup steps the SDK runs directly against the venue with the session credential. No signed step returns to the backend.hmac— every trading action. The SDK signs each venue request with the API key and the backend relays the signed request to the venue.
The SIWE session
The user’s EVM-compatible signer — provided to the SDK viauserWallet (createPerpsClient) or setUserWallet() — signs one thing on Ondo: the SIWE login challenge.
createAction for siweLogin returns a SiweActionStep carrying the challenge ({ challengeId, message }). The wallet personal_signs the message, and the resulting SiweSignedActionStep ({ action, siwe, signature }) is submitted via executeAction. In exchange the venue issues a session credential the SDK holds client-side — it is never shared with LI.FI.
The API key
The API key is a venue trading key the SDK creates during setup (theregisterApiKey step) using the session credential. It is stored client-side and is used to sign every trading request thereafter.
What the API key does:
- Signs every trading action —
placeOrder,placeTriggerOrder,cancelOrder,cancelAllOrders,placeTwapOrder,cancelTwapOrder, andupdateLeverage.
- It does not sign the SIWE login — that requires the user’s wallet.
- It is not transmitted to the LI.FI backend. Only the per-request HMAC material (
{ keyId, timestampMs, signature }) crosses the wire; the backend builds the venue’s transport headers from it at relay time.
- The SDK creates the key lazily on first HMAC use, or eagerly when the user runs the
registerApiKeysetup step — both paths share the same creation logic, so whichever runs first satisfies the other. - The venue registers it under the name
lifi-perpswith scopes['trade']. - Ondo caps an account at 10 API keys. When the account is already at the cap, the SDK reclaims the oldest key it owns (matched by name
lifi-perps) before creating a new one — oldest bycreatedAt, ties broken by the smallerkeyId. If nolifi-perps-named key is available to reclaim, key creation fails. - If the venue rejects an HMAC-signed request with
Unauthorized(the key was revoked or expired venue-side), the SDK evicts its local copy so the next trading action re-creates one automatically.
The HMAC-signed trading relay
Trading actions are HMAC-relayed.createAction returns one or more HmacActionSteps, each an unsigned venue request:
hmac field, yielding a HmacSignedActionStep:
request.body is a pre-serialized string that transits verbatim — it is the exact byte string the HMAC signature covers, and it is never re-serialized downstream.
Signature and relay: the SDK computes HMAC-SHA256(apiSecret, ${timestampMs}${METHOD}${pathWithQuery}${body}), hex-encoded, where METHOD is upper-cased and the fields are concatenated with no separators. timestampMs is stamped immediately before executeAction; Ondo enforces a 30-second signing window between that timestamp and the venue receiving the request. The backend relays the signed request to the venue as ONDO-KEY-ID, ONDO-TIMESTAMP, and ONDO-SIGN transport headers and returns the venue’s result. There is no retry on failure — a re-sent request would carry the same frozen timestamp and fail the venue’s window check.
Client-only session steps
createDepositAddress, acceptProviderTerms, registerApiKey, and setReferrer are session steps. The SDK performs the venue call itself with the client-held session credential and skips executeAction, but the three fall into two different shapes:
acceptProviderTermsandregisterApiKeyare bare markers — the backend step carries no request material at all. The SDK authors the venue call itself:acceptProviderTermsposts{ termsVersion, privacyVersion }(both currently1) toPOST /v1/agreement;registerApiKeyruns the same key-creation logic described above.createDepositAddresscarries a fixed, backend-authored deposit policy ({ network: 'ethereum', symbol: 'USDC', depositDestination: { wallet: 'margin' } }) but no venue path or credentials. The SDK reads the account (GET /v1/account) to get the venueaccountID, provisions the address (POST /v1/provision_address) with the policy and that id, then reads the result back (POST /v1/wallet/deposit_address/list) to confirm the venue can serve it.setReferreris not a bare marker — the backend authors a real request (POST /v1/account/referralwith{ code }), and the SDK submits it as-is using the session credential rather than an HMAC signature. It runs as an internal setup step (not user-facing) once the account has no referrer attached.
The session credential’s lifecycle
The session credential has no refresh flow. The SDK evicts it in two cases and both fall back to a logged-out account state, which causes the nextcheckSetup() to re-stage siweLogin:
- Client-side expiry — the token store checks
expirationSecs * 1000 <= Date.now()on every read and drops an expired token before returning it. - Server-side revocation — a mid-call
401from the venue surfaces as a session-expired error; the SDK evicts the stored token immediately rather than letting a locally-valid-looking token soft-lock the UI.