Skip to content

SDK: Lighter Adapter#983

Merged
pedromcunha merged 15 commits into
mainfrom
feature/lighter-adapter
Apr 24, 2026
Merged

SDK: Lighter Adapter#983
pedromcunha merged 15 commits into
mainfrom
feature/lighter-adapter

Conversation

@pedromcunha
Copy link
Copy Markdown
Member

No description provided.

@pedromcunha pedromcunha requested a review from ted-palmer April 23, 2026 01:10
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
relay-sdk-demo Ready Ready Preview, Comment Apr 23, 2026 10:36pm

Request Review

ted-palmer
ted-palmer previously approved these changes Apr 23, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 23, 2026

Claude finished @pedromcunha's task in 2m 43s —— View job


📝 Docs Drift Check — Confidence: MEDIUM

What changed: Adds a new public wallet adapter package @relayprotocol/relay-lighter-wallet-adapter (adaptLighterWallet) and extends the SDK with a new lvm vmType and LvmReceipt type so Relay can route deposits into Lighter via the SDK rather than the raw API flow.

Docs impact: The adapters reference page currently lists every officially-maintained adapter (SVM, Bitcoin, Viem, Ethers, Sui, Tron) and enumerates supported vmType values — both are now stale. The existing Lighter API integration guide is API-only and should cross-reference the SDK path as an alternative.

Affected pages (paths inside relayprotocol/relay-docs):

  • references/relay-kit/sdk/adapters.mdx — update (add lvm to vmType enum, add Lighter to adapter list, add Lighter code example)
  • references/api/api_guides/lighter-support.mdx — update (add a short pointer to the SDK adapter as an alternative to the raw API flow)

Draft:

> Replace row: the **vmType** row inside the AdaptedWallet interface table

| **vmType**                    | A string representing a supported vmType (`evm`  `svm`  `bvm`  `suivm` `tvm` `lvm`)                                                   ||
> Insert after: the `[Tron Adapter](...)` paragraph under `### What adapters are available out of the box?`

[Lighter Adapter](https://github.com/relayprotocol/relay-kit/tree/main/packages/relay-lighter-wallet-adapter): This adapter gives the SDK the ability to deposit to [Lighter](https://lighter.exchange/) from any Relay-supported chain. It owns the full Lighter session lifecycle — resolving the user's Lighter account index, generating an in-memory API key, and registering it via `changeApiKey` on first transfer. Integrators who already run their own Lighter session (backend-provisioned key, wallet-level integration) can pass a pre-built `signerClient` to bypass the bootstrap entirely.
> Insert after: the ` ```typescript Tron ` block inside the `<CodeGroup>` under `### How can I use an adapter?`, before the closing `</CodeGroup>`

```typescript Lighter
import { getClient, getQuote } from '@relayprotocol/relay-sdk'
import { adaptLighterWallet } from '@relayprotocol/relay-lighter-wallet-adapter'
import { createWalletClient, custom } from 'viem'
import { mainnet } from 'viem/chains'

// Lighter deposits are authorized by an L1 (EVM) signature, so the adapter
// only needs the user's connected EVM address and a signMessage callback.
const walletClient = createWalletClient({
  chain: mainnet,
  transport: custom(window.ethereum!)
})
const account = walletClient.account!

const adaptedWallet = adaptLighterWallet({
  l1Address: account.address,
  signL1Message: (message) =>
    walletClient.signMessage({ account, message })
})

const options = ... // define this based on getQuote options (destinationChainId: 3586256, recipient: <Lighter account index>)

const quote = await getClient().actions.getQuote(options)

getClient().actions.execute({
  quote,
  wallet: adaptedWallet,
  onProgress: ({ steps, fees, breakdown, currentStep, currentStepItem, txHashes, details }) => {
    // custom handling
  }
})
```
> Insert after: the `# API Access` section's closing paragraph, before `## Lighter-Specific API Parameters`

<Tip>
  If you're integrating via the SDK, the [Lighter wallet adapter](/references/relay-kit/sdk/adapters) wraps this flow — it resolves the user's Lighter account index, manages the API key lifecycle, and submits the L2 transfer on your behalf.
</Tip>

Needs human input (optional):

  • The adapters page uses ### as its top-level heading, which conflicts with §2.2 of the docs style guide (SDK references should open with ##). I deliberately left the headings as-is to keep this edit surgical; a cleanup pass is a separate concern.
  • The Lighter adapter ships four API-key lifecycle strategies (fresh bootstrap, persistent storage, externally-managed key, pre-built signer). Only the default bootstrap path is shown in the CodeGroup example above, matching the density of other adapters in the list. If you want the full strategy matrix in the docs, it warrants its own page (e.g. references/relay-kit/sdk/adapters/lighter.mdx) rather than expanding the overview.
  • The recipient param on the API guide is a Lighter account index (not an address). When deposits originate from the SDK, integrators still need to resolve that index themselves; adding a short paragraph on how the adapter exposes it via adaptedWallet.address() may help but is a judgment call.

To publish as a draft PR in relayprotocol/relay-docs, reply on this PR with:

/publish-docs-pr

A separate workflow will open a draft PR attributed to you. Edit the draft freely before marking it ready for review.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 23, 2026

Claude finished @pedromcunha's task in 3m 20s —— View job


Code Review — 1 issue found

  • Gather context (diff, commits, PR description)
  • Read changed files
  • Analyze correctness, security, error handling
  • Post review

  1. NOTEError handling: silent fallback to nonce 0 in changeApiKey
    packages/relay-lighter-wallet-adapter/src/adapter.ts:490-504 — when transactionApi.getNextNonce() returns something the adapter can't coerce to a finite number (missing field, malformed response, transient upstream issue), the code silently falls back to nonce: 0:

    const rawNonce = nextNonceResponse?.nonce
    const nonceForChangeKey =
      typeof rawNonce === 'number' ? rawNonce : Number(rawNonce ?? 0)
    ...
    nonce: Number.isFinite(nonceForChangeKey) ? nonceForChangeKey : 0

    For a fresh API-key slot 0 is correct, but if a re-registration path ever hits this code, passing 0 produces a signature Lighter will reject — and the resulting rejection message won't point at the real cause (missing/garbled nonce response). Per the repo guideline on avoiding fallback defaults on critical values, it would be safer to throw when rawNonce can't be parsed into a finite number.

    Fix this →


Reviewed 21 files, 1661 additions / 27 deletions. Checked the new adapter (bootstrap + pre-built signer paths), the demo proxy (demo/pages/api/lighter/[...path].ts), the SDK type extensions for LvmReceipt and the new transfer action shape, and the adapter test suite.

Other things checked that look fine:

  • Demo proxy correctly strips x-api-key collisions (Node lowercases req.headers) and never exposes LIGHTER_API_KEY client-side; origin allowlist is optional by design.
  • SDK type unions (AdaptedWallet, TransactionConfirmationError, Execute, transaction.ts) are extended with LvmReceipt consistently — no union branches missed.
  • adapter.ts bootstrap failure handling: the signerPromise.catch(() => { signerPromise = null }) reset-on-failure pattern is correct — callers still observe the original rejection and retries can re-bootstrap.
  • pollLighterTransaction correctly distinguishes findable vs committed, maps both numeric and string status forms, throws on FAILED/REJECTED, and times out.
  • Pre-built signer path bypasses the SDK dynamic import cleanly — the test suite's choice not to mock loadLighterSdk is a useful regression guard.
    • Branch: feature/lighter-adapter

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 23, 2026

Claude finished @pedromcunha's task in 3m 9s —— View job


Code Review — No issues found.

  • Gather context (diff, commits, PR description)
  • Read changed files
  • Analyze correctness, security, error handling, repo guidelines
  • Post review

Reviewed 21 files, 1661 additions / 27 deletions. Checked the new relay-lighter-wallet-adapter package (bootstrap and pre-built signer paths), the demo proxy (demo/pages/api/lighter/[...path].ts), the SDK type extensions for LvmReceipt / lvm vmType, and the adapter test suite.

Things checked that look correct:

  • SDK type plumbingLvmReceipt is added consistently across AdaptedWallet, Execute, TransactionConfirmationError, sendTransactionSafely, with no union branches missed; 'lvm' was already in ChainVM.
  • TransactionStepItem.data.action is typed narrowly (type: 'transfer') and the adapter rejects anything else in handleSendTransactionStep.
  • Demo proxy — origin allowlist CORS, forwards hop-by-hop-stripped headers, overrides x-api-key server-side after the forward loop (so client-supplied X-API-Key can't survive the lowercase re-injection), strips content-encoding/content-length correctly. The [...path] catch-all guarantees req.url starts with /api/lighter/, so the replace(PATH_PREFIX, '') suffix is always path-prefixed — no SSRF vector.
  • Bootstrap signer lifecycle — the signerPromise.catch(() => { signerPromise = null }) pattern correctly resets on failure while still surfacing the original rejection to awaiters. The deliberate omission of ensureWasmClient() on the keygen client (per the comment, to avoid the ChangePubKey Sig being produced with the dummy key) is a subtle correctness fix worth the inline note.
  • pollLighterTransaction — separates findable vs committed semantics, maps both numeric and string statuses, throws on FAILED/REJECTED, times out cleanly, tolerates transient getTransaction throws.
  • Pre-built signer path — cleanly bypasses loadLighterSdk(); the test suite's choice to not mock that dynamic import is a nice regression guard for "signerClient must skip the SDK entirely".
  • Discriminated union on optionssignerClient + accountIndex are paired via TypeScript so you can't supply one without the other.

One prior note (silent fallback to nonce: 0 in changeApiKey, adapter.ts:490-504) was raised in the previous review and dismissed. Not re-flagging.
· Branch: feature/lighter-adapter

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 23, 2026

Claude finished @pedromcunha's task in 1m 46s —— View job


📝 Docs Drift Check — Confidence: MEDIUM

What changed: Adds a new public wallet adapter package @relayprotocol/relay-lighter-wallet-adapter (adaptLighterWallet) and extends the SDK with a new lvm vmType and LvmReceipt type so Relay can route deposits into Lighter via the SDK rather than the raw API flow.

Docs impact: The adapters reference page currently lists every officially-maintained adapter (SVM, Bitcoin, Viem, Ethers, Sui, Tron) and enumerates supported vmType values — both are now stale. The existing Lighter API integration guide is API-only and should cross-reference the SDK path as an alternative.

Affected pages (paths inside relayprotocol/relay-docs):

  • references/relay-kit/sdk/adapters.mdx — update (add lvm to vmType enum, add Lighter to adapter list, add Lighter code example)
  • references/api/api_guides/lighter-support.mdx — update (add a short pointer to the SDK adapter as an alternative to the raw API flow)

Draft:

> Replace row: the **vmType** row inside the AdaptedWallet interface table

| **vmType**                    | A string representing a supported vmType (`evm`  `svm`  `bvm`  `suivm` `tvm` `lvm`)                                                   ||
> Insert after: the `[Tron Adapter](...)` paragraph under `### What adapters are available out of the box?`

[Lighter Adapter](https://github.com/relayprotocol/relay-kit/tree/main/packages/relay-lighter-wallet-adapter): This adapter gives the SDK the ability to deposit to [Lighter](https://lighter.exchange/) from any Relay-supported chain. It owns the full Lighter session lifecycle — resolving the user's Lighter account index, generating an in-memory API key, and registering it via `changeApiKey` on first transfer. Integrators who already run their own Lighter session (backend-provisioned key, wallet-level integration) can pass a pre-built `signerClient` to bypass the bootstrap entirely.
> Insert after: the ` ```typescript Tron ` block inside the `<CodeGroup>` under `### How can I use an adapter?`, before the closing `</CodeGroup>`

```typescript Lighter
import { getClient, getQuote } from '@relayprotocol/relay-sdk'
import { adaptLighterWallet } from '@relayprotocol/relay-lighter-wallet-adapter'
import { createWalletClient, custom } from 'viem'
import { mainnet } from 'viem/chains'

// Lighter deposits are authorized by an L1 (EVM) signature, so the adapter
// only needs the user's connected EVM address and a signMessage callback.
const walletClient = createWalletClient({
  chain: mainnet,
  transport: custom(window.ethereum!)
})
const account = walletClient.account!

const adaptedWallet = adaptLighterWallet({
  l1Address: account.address,
  signL1Message: (message) =>
    walletClient.signMessage({ account, message })
})

const options = ... // define this based on getQuote options (destinationChainId: 3586256, recipient: <Lighter account index>)

const quote = await getClient().actions.getQuote(options)

getClient().actions.execute({
  quote,
  wallet: adaptedWallet,
  onProgress: ({ steps, fees, breakdown, currentStep, currentStepItem, txHashes, details }) => {
    // custom handling
  }
})
```
> Insert after: the `# API Access` section's closing paragraph, before `## Lighter-Specific API Parameters`

<Tip>
  If you're integrating via the SDK, the [Lighter wallet adapter](/references/relay-kit/sdk/adapters) wraps this flow — it resolves the user's Lighter account index, manages the API key lifecycle, and submits the L2 transfer on your behalf.
</Tip>

Needs human input (optional):

  • The adapters page uses ### as its top-level heading, which conflicts with §2.2 of the docs style guide (SDK references should open with ##). I deliberately left the headings as-is to keep this edit surgical; a cleanup pass is a separate concern.
  • The Lighter adapter ships four API-key lifecycle strategies (fresh bootstrap, persistent storage, externally-managed key, pre-built signer). Only the default bootstrap path is shown in the CodeGroup example above, matching the density of other adapters in the list. If you want the full strategy matrix in the docs, it warrants its own page (e.g. references/relay-kit/sdk/adapters/lighter.mdx) rather than expanding the overview.
  • The recipient param on the API guide is a Lighter account index (not an address). When deposits originate from the SDK, integrators still need to resolve that index themselves; adding a short paragraph on how the adapter exposes it via adaptedWallet.address() may help but is a judgment call.

To publish as a draft PR in relayprotocol/relay-docs, reply on this PR with:

/publish-docs-pr

A separate workflow will open a draft PR attributed to you. Edit the draft freely before marking it ready for review.

@pedromcunha
Copy link
Copy Markdown
Member Author

/publish-docs-pr

@github-actions
Copy link
Copy Markdown
Contributor

🚚 Docs Draft PR — starting publish to relayprotocol/relay-docs, requested by @pedromcunha. I'll post the PR link here when it's ready.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 23, 2026

Claude finished @pedromcunha's task in 1m 54s —— View job


Publishing docs update — done

  • Load drift comment draft
  • Read docs style guide
  • Read target docs files
  • Apply edits to references/relay-kit/sdk/adapters.mdx
  • Apply edits to references/api/api_guides/lighter-support.mdx
  • Verify style-guide conformance
  • Emit publish summary

Materialized the drift draft into two docs files — references/relay-kit/sdk/adapters.mdx (vmType row + Lighter adapter paragraph + Lighter <CodeGroup> entry) and references/api/api_guides/lighter-support.mdx (cross-reference <Tip> between # API Access and ## Lighter-Specific API Parameters). A follow-up workflow opens the docs PR.

Deviations from strict style guide:

  • Left the adapters page's ### top-level headings in place (§2.2 violation — flagged in the drift draft as deliberately out-of-scope for a surgical edit).
  • Left lighter-support.mdx's existing # H1 headings (# Prerequisites, # API Access) untouched (pre-existing §2.2 violations, not introduced by this change).
    · Branch: feature/lighter-adapter

@github-actions
Copy link
Copy Markdown
Contributor

Docs Draft PR opened: relayprotocol/relay-docs#304

@pedromcunha is assigned on the draft PR. Review and mark ready when it's good.

@pedromcunha pedromcunha merged commit 670737f into main Apr 24, 2026
6 checks passed
@pedromcunha pedromcunha deleted the feature/lighter-adapter branch April 24, 2026 13:32
pedromcunha added a commit to relayprotocol/relay-docs that referenced this pull request Apr 24, 2026
* docs(drift): draft from relayprotocol/relay-kit#983

Requested by @pedromcunha via /publish-docs-pr on relayprotocol/relay-kit#983

* lighter sdk doc tweaks

* Improve the lighter integration guide

* Fix PR review feedback

* Fix lighter docs nits and feedback

* Fix lighter docs nits and feedback

* Fix lighter docs nits and feedback

* Fix lighter docs nits and feedback

---------

Co-authored-by: relay-docs-bot <relay-docs-bot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants