Skip to content

[LWDM] feat(coin-tester): add the NEAR coin-tester - #20297

Open
pawell24 wants to merge 6 commits into
feat/LIVE-34284-near-coinmodule-api-migrationfrom
feat/LIVE-34293-near-coin-tester
Open

[LWDM] feat(coin-tester): add the NEAR coin-tester#20297
pawell24 wants to merge 6 commits into
feat/LIVE-34284-near-coinmodule-api-migrationfrom
feat/LIVE-34293-near-coin-tester

Conversation

@pawell24

@pawell24 pawell24 commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

📝 Description

Adds @ledgerhq/coin-tester-near, a deterministic integration tester for the NEAR family. It boots a local near-sandbox node and runs one scenario through both the legacy account bridge and the generic coin-framework bridge, so the two surfaces are compared against each other on every run.

The scenario covers a transfer to an existing named account, a transfer to a fresh implicit account, and the full staking cycle: delegate, undelegate, and withdraw after the unlock period.

No Docker. near-sandbox downloads a native neard build for the host platform, so it runs on the standard CI runner and on a laptop with no extra setup. staking_pool.wasm is pinned to a commit and verified against its sha256, so neither upstream nor a stale cache can change the contract the scenario delegates to.

Three changes outside the tester package are needed for the generic-adapter half of the run:

  • families/near/bridge/api.ts (new): NEAR names its staking modes stake / unstake / withdraw, while the framework's default whitelist knows stake / unstake / finalize_unstake and throws on anything else. Without the mapping, withdraw cannot be built through the framework at all.
  • generic-coin-framework/createTransaction.ts: NEAR joins the Cardano branch, since a NEAR nonce belongs to an access key rather than the account, so the default transaction must be signable without callers setting a sequence.
  • generic-coin-framework/utils.ts: buildOptimisticOperation converted fees with BigNumber.toString(), which switches to exponential notation from 1e21 up and cannot be parsed by BigInt. At NEAR's 24 decimals a staking fee crosses that threshold at 0.0015, so every staked operation threw. Now uses toFixed(), with a regression test asserting the boundary directly.

The last one is a shared-framework fix affecting any family with a large-magnitude fee. It is bundled here rather than split out because the tester is red without it.

What the first end-to-end run caught

Four contract mismatches that unit tests had missed, all wrong assumptions about the indexer contract rather than bugs in the coin module:

Finding Why unit tests missed it
/v3/stats must be stubbed getGasPrice's node-RPC fallback only covers a response without a gas_price, not a request that throws
staking-deposits returns a bare array the module maps over the body itself, with no { data } envelope
loadBridgeApi is required the framework reads computeIntentType from the loader registry, not from the caller
a fresh account is not immediately final the module queries with finality: "final" while a new account is first visible only optimistically

Notes for reviewers

  • Most of the diff is test code and the generated lockfile entry. The shipped logic is bridge/api.ts (30 lines) plus two small framework edits.
  • families/near/bridge/api.ts gets the bridge/api.test.ts that the other nine families already have.
  • Runtime is about 52 s per strategy. The coin-tester job is continue-on-error: true, so it reports without gating the merge.
  • Based on [LWDM] feat(coin-near): add CoinModuleApi for native NEAR and staking #20296. The base branch will retarget to develop automatically once that merges.

🔗 Context

  • JIRA / GitHub issue: LIVE-34293
  • ADR (if any): n/a

Copilot AI review requested due to automatic review settings August 2, 2026 14:24
@live-github-bot live-github-bot Bot added common Has changes in live-common automation CI/CD stuff shared-lib Label added for automated tagging of PRs labels Aug 2, 2026
@live-github-bot live-github-bot Bot changed the title feat(coin-tester): add the NEAR coin-tester [LWDM] feat(coin-tester): add the NEAR coin-tester Aug 2, 2026
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Web Tools Build Status

Build Status Deployment
Web Tools Build ✅ Deployed https://web-tools-8qwzqjc4k-ledger-hq-prd.vercel.app
Native Storybook Build ✅ Deployed https://native-ui-storybook-fcvlcalzw-ledger-hq-prd.vercel.app
React Storybook Build ✅ Deployed https://react-ui-storybook-4vsfsknvj-ledger-hq-prd.vercel.app

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new deterministic NEAR coin-tester module and the minimal shared framework hooks needed to run the same scenario through both the legacy account bridge and the generic coin-framework bridge.

Changes:

  • Introduces @ledgerhq/coin-tester-near (near-sandbox-backed) with an MSW indexer stub, local signer, and a full send + staking lifecycle scenario.
  • Adds NEAR bridge API intent mapping (withdrawfinalize_unstake) and wires it into the coin-module loader registry.
  • Fixes generic framework optimistic-op fee parsing for large BigNumber magnitudes and updates createTransaction defaults to support NEAR’s nonce semantics.

Reviewed changes

Copilot reviewed 25 out of 26 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
pnpm-lock.yaml Adds dependency graph entries for the new NEAR coin-tester package and near-sandbox transitive deps.
package.json Adds coin:tester:near script to run the new tester workspace.
libs/ledger-live-common/src/families/near/bridge/api.ts Adds NEAR-specific intent-type mapping so the generic framework can build withdraw operations.
libs/ledger-live-common/src/families/near/bridge/api.test.ts Unit tests for the NEAR bridge API intent mapping and default export wiring.
libs/ledger-live-common/src/coin-modules/loaders.ts Registers NEAR loadBridgeApi so the generic framework can resolve computeIntentType.
libs/ledger-live-common/src/bridge/generic-coin-framework/utils.ts Fixes fee conversion for optimistic ops by avoiding exponential notation when converting to BigInt.
libs/ledger-live-common/src/bridge/generic-coin-framework/utils.test.ts Adds regression coverage for the BigNumber 1e21 boundary that previously broke BigInt parsing.
libs/ledger-live-common/src/bridge/generic-coin-framework/createTransaction.ts Treats NEAR like Cardano regarding default nonce/sequence expectations (signable default tx).
libs/coin-tester-modules/coin-tester-near/tsconfig.json TypeScript config for the new coin-tester workspace.
libs/coin-tester-modules/coin-tester-near/tsconfig.build.json Build-time TS config excluding tests and test folders.
libs/coin-tester-modules/coin-tester-near/src/stakingPool.ts Downloads/verifies a pinned staking-pool WASM and deploys/pings it in the sandbox.
libs/coin-tester-modules/coin-tester-near/src/signer.ts Implements local signers compatible with both legacy bridge and generic framework signing contracts.
libs/coin-tester-modules/coin-tester-near/src/scenarii/near.ts Defines the deterministic NEAR scenario (2 transfers + stake/unstake/withdraw), plus sandbox/indexer lifecycle.
libs/coin-tester-modules/coin-tester-near/src/scenarii.test.ts Runs the same NEAR scenario for both strategies (legacy and generic-adapter).
libs/coin-tester-modules/coin-tester-near/src/sandbox.ts Starts near-sandbox on a free port, provides RPC helpers, account funding, and fast-forwarding.
libs/coin-tester-modules/coin-tester-near/src/indexer.ts MSW stubs for indexer endpoints (history/stats/deposits/validators) backed by sandbox state.
libs/coin-tester-modules/coin-tester-near/src/helpers.ts Bridge wiring for both strategies and legacy-bridge adaptation to GenericTransaction.
libs/coin-tester-modules/coin-tester-near/src/fixtures.ts Deterministic constants + LiveConfig coin-config pointing to sandbox and MSW indexer host.
libs/coin-tester-modules/coin-tester-near/README.md Documentation on running the tester and what it covers.
libs/coin-tester-modules/coin-tester-near/package.json Defines the new workspace, deps, scripts, and exports for coin-tester-near.
libs/coin-tester-modules/coin-tester-near/jest.config.ts Jest config for node-based integration tests via SWC.
libs/coin-tester-modules/coin-tester-near/CHANGELOG.md Initializes changelog for the new package.
libs/coin-tester-modules/coin-tester-near/.unimportedrc.json Adds legacy unimported configuration for the new package.
libs/coin-tester-modules/coin-tester-near/.gitignore Ignores the downloaded staking pool WASM cache directory.
.github/workflows/test-coin-tester.yml Adds near to the coin-tester workflow matrix env list.
.changeset/near-coin-tester.md Changeset bumping @ledgerhq/coin-tester-near and @ledgerhq/live-common.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread libs/ledger-live-common/src/bridge/generic-coin-framework/utils.test.ts Outdated
Comment thread libs/coin-tester-modules/coin-tester-near/src/indexer.ts Outdated
Comment thread libs/coin-tester-modules/coin-tester-near/package.json
Comment thread libs/coin-tester-modules/coin-tester-near/package.json Outdated
Comment thread libs/coin-tester-modules/coin-tester-near/.unimportedrc.json Outdated
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Rsdoctor Bundle Diff Analysis

Found 7 projects in monorepo, 7 projects with changes.

📊 Quick Summary
Project Total Size Change
desktop-main 2.3 MB -
desktop-preloader 7.1 KB -
desktop-renderer 80.7 MB -
desktop-webviewDappPreloader 0 B -
desktop-webviewPreloader 0 B -
desktop-workers 36.8 KB -
mobile 261.5 MB -
📋 Detailed Reports (Click to expand)

📁 desktop-main

Path: rsdoctor/desktop-main/rsdoctor-data.json

⚠️ No baseline data found - Unable to perform comparison analysis

Metric Current Baseline Change
📊 Total Size 2.3 MB - -
📄 JavaScript 2.2 MB - -
🎨 CSS 0 B - -
🌐 HTML 0 B - -
📁 Other Assets 135.3 KB - -

📁 desktop-preloader

Path: rsdoctor/desktop-preloader/rsdoctor-data.json

⚠️ No baseline data found - Unable to perform comparison analysis

Metric Current Baseline Change
📊 Total Size 7.1 KB - -
📄 JavaScript 5.3 KB - -
🎨 CSS 0 B - -
🌐 HTML 0 B - -
📁 Other Assets 1.8 KB - -

📁 desktop-renderer

Path: rsdoctor/desktop-renderer/rsdoctor-data.json

⚠️ No baseline data found - Unable to perform comparison analysis

Metric Current Baseline Change
📊 Total Size 80.7 MB - -
📄 JavaScript 29.3 MB - -
🎨 CSS 183.1 KB - -
🌐 HTML 1.8 KB - -
📁 Other Assets 51.2 MB - -

📁 desktop-webviewDappPreloader

Path: rsdoctor/desktop-webviewDappPreloader/rsdoctor-data.json

⚠️ No baseline data found - Unable to perform comparison analysis

Metric Current Baseline Change
📊 Total Size 0 B - -
📄 JavaScript 0 B - -
🎨 CSS 0 B - -
🌐 HTML 0 B - -
📁 Other Assets 0 B - -

📁 desktop-webviewPreloader

Path: rsdoctor/desktop-webviewPreloader/rsdoctor-data.json

⚠️ No baseline data found - Unable to perform comparison analysis

Metric Current Baseline Change
📊 Total Size 0 B - -
📄 JavaScript 0 B - -
🎨 CSS 0 B - -
🌐 HTML 0 B - -
📁 Other Assets 0 B - -

📁 desktop-workers

Path: rsdoctor/desktop-workers/rsdoctor-data.json

⚠️ No baseline data found - Unable to perform comparison analysis

Metric Current Baseline Change
📊 Total Size 36.8 KB - -
📄 JavaScript 36.8 KB - -
🎨 CSS 0 B - -
🌐 HTML 0 B - -
📁 Other Assets 0 B - -

📁 mobile

Path: rsdoctor/mobile/rsdoctor-data.json

⚠️ No baseline data found - Unable to perform comparison analysis

Metric Current Baseline Change
📊 Total Size 261.5 MB - -
📄 JavaScript 110.5 MB - -
🎨 CSS 0 B - -
🌐 HTML 0 B - -
📁 Other Assets 151.0 MB - -

Generated by Rsdoctor GitHub Action

Copilot AI review requested due to automatic review settings August 2, 2026 14:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 26 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file
Suppressed comments (3)

libs/coin-tester-modules/coin-tester-near/src/indexer.ts:146

  • Number(...) can yield NaN when limit is present but not numeric (e.g. ?limit=foo). In that case the >= limit check in historyFor will never be true and the stub may return an unbounded history. Safer to default to 25 unless the parsed limit is finite.
      const limit = Number(new URL(request.url).searchParams.get("limit") ?? 25);
      const data = await historyFor(rpc, String(params.address), limit);

libs/coin-tester-modules/coin-tester-near/src/sandbox.ts:55

  • The RPC helper always calls response.json() without checking response.ok. If the sandbox returns a non-2xx response or a non-JSON body, this will throw a low-signal error and can also keep the socket busy. Consider handling HTTP errors explicitly and surfacing status + body for easier debugging.
  const rpc = async <T>(method: string, params: unknown): Promise<T> => {
    const response = await fetch(rpcUrl, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ jsonrpc: "2.0", id: "coin-tester", method, params }),
    });
    const body = (await response.json()) as { result?: T; error?: unknown };

    if (body.error) {
      throw new Error(`near-sandbox ${method}: ${JSON.stringify(body.error)}`);
    }

    return body.result as T;
  };

libs/coin-tester-modules/coin-tester-near/src/indexer.ts:211

  • This constant allowlist is recreated on every unhandled request. Prefer a module-level Set and .has() for constant membership checks (clearer intent and avoids repeated array allocation).
  server.listen({
    onUnhandledRequest: request => {
      const hostname = new URL(request.url).hostname;
      // Allow requests to the local sandbox node to pass through to the real node.
      if (["127.0.0.1", "localhost"].includes(hostname)) return;
      throw new Error(`Unhandled request: ${request.method} ${request.url}`);
    },

Copilot AI review requested due to automatic review settings August 2, 2026 17:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 27 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file
Suppressed comments (1)

libs/coin-tester-modules/coin-tester-near/src/sandbox.ts:116

  • tearDown() uses Promise.race([sandbox.tearDown(), timeout]). If the timeout wins, sandbox.tearDown() keeps running in the background and may later reject, which would become an unhandled promise rejection (and can fail the Jest process). Wrap sandbox.tearDown() with a .catch(...) before racing so any late rejection is handled.
    async tearDown() {
      // The node occasionally keeps a socket open and never resolves; the temp home is disposable,
      // so a stuck shutdown must not hold the whole suite hostage.
      let timer: NodeJS.Timeout | undefined;
      await Promise.race([
        sandbox.tearDown(),
        new Promise<void>(resolve => {
          timer = setTimeout(() => {
            console.warn("coin-tester-near: sandbox teardown timed out, continuing");
            resolve();
          }, 15_000);
        }),
      ]);

Copilot AI review requested due to automatic review settings August 2, 2026 17:55
@pawell24
pawell24 force-pushed the feat/LIVE-34293-near-coin-tester branch from 60ebfda to 35df985 Compare August 2, 2026 17:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 27 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file
Suppressed comments (1)

libs/ledger-live-common/src/families/near/bridge/api.ts:14

  • JSON.stringify(mode) can itself throw (e.g. when mode is a bigint) or return undefined (e.g. symbol), which would either mask the intended error or produce an unhelpful message. Since mode is unknown here, it’s safer to stringify with a fallback to String(mode).
  if (typeof mode !== "string") {
    throw new TypeError(`Unsupported transaction mode: ${JSON.stringify(mode)}`);
  }

Copilot AI review requested due to automatic review settings August 2, 2026 18:32
@pawell24
pawell24 force-pushed the feat/LIVE-34293-near-coin-tester branch from 35df985 to 0e716a3 Compare August 2, 2026 18:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 27 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file
Suppressed comments (1)

libs/coin-tester-modules/coin-tester-near/src/sandbox.ts:112

  • Promise.race([sandbox.tearDown(), timeout]) will reject if sandbox.tearDown() throws, and in that case the timeout handle is never cleared. Since the intent here is to avoid teardown issues holding the suite hostage, it’s safer to (1) swallow/console.warn teardown failures and (2) clear the timer in a finally block so it’s always cleaned up.
      await Promise.race([
        sandbox.tearDown(),
        new Promise<void>(resolve => {
          timer = setTimeout(() => {
            console.warn("coin-tester-near: sandbox teardown timed out, continuing");

Copilot AI review requested due to automatic review settings August 2, 2026 20:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 27 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file
Suppressed comments (4)

libs/ledger-live-common/src/families/near/bridge/api.ts:24

  • Same as above: the unsupported-mode error message would be clearer if it included the family name (e.g. Unsupported NEAR transaction mode: ...) for consistency with other bridge APIs.
      return "finalize_unstake";
    default:
      throw new Error(`Unsupported transaction mode: ${mode}`);
  }

libs/ledger-live-common/src/families/near/bridge/api.ts:14

  • The thrown error message here doesn't include the family name, unlike the other bridge APIs (e.g. Unsupported Solana transaction mode: ... in families/solana/bridge/api.ts). Including NEAR in the message would make framework-side debugging much easier.

This issue also appears on line 21 of the same file.

  if (typeof mode !== "string") {
    throw new TypeError(`Unsupported transaction mode: ${JSON.stringify(mode)}`);
  }

libs/coin-tester-modules/coin-tester-near/package.json:38

  • The package's exports map is missing the standard ./* conditional export used by the other coin-tester modules (e.g. coin-tester-solana, coin-tester-stellar). Without it, consumers relying on the @ledgerhq/source condition (and the require/default split) won't resolve subpath imports consistently.
  "exports": {
    "./lib/*": "./lib/*.js",
    "./lib-es/*": "./lib-es/*.js",
    "./package.json": "./package.json"
  },

libs/coin-tester-modules/coin-tester-near/src/sandbox.ts:116

  • Promise.race doesn't cancel the losing promise. If sandbox.tearDown() eventually rejects after the timeout branch wins, it can surface as an unhandled rejection and fail the Jest run. Attaching a .catch prevents that while keeping the timeout behavior.
      await Promise.race([
        sandbox.tearDown(),
        new Promise<void>(resolve => {

@pawell24
pawell24 marked this pull request as ready for review August 3, 2026 08:16
@pawell24
pawell24 requested review from a team as code owners August 3, 2026 08:16
Copilot AI review requested due to automatic review settings August 3, 2026 12:59
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

⚠️ E2E tests are required

Changes detected require e2e testing before merge (even before asking for any review).

🖥️ Desktop

-> Run Desktop E2E

  • Select "Run workflow"
  • Branch: feat/LIVE-34293-near-coin-tester
  • Device: nanoSP or stax

📱 Mobile

-> Run Mobile E2E

  • Select "Run workflow"
  • Branch: feat/LIVE-34293-near-coin-tester
  • Device: nanoX

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 27 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file
Suppressed comments (3)

pnpm-lock.yaml:14295

  • The lockfile includes changes unrelated to adding the NEAR coin-tester (e.g., the @apollo/server@4.12.2 deprecated metadata text changed while the package version stayed the same). This suggests the lockfile was regenerated with different tooling/metadata and adds noise that makes dependency reviews harder. Please try to regenerate the lockfile so it only reflects the new workspace/deps introduced by this PR (or explain why these extra deltas are unavoidable).
    pnpm-lock.yaml:22880
  • The lockfile changes @storybook/addon-react-native-web@0.0.29’s peerDependencies.webpack from '*' to '^5.89.0' without any corresponding version bump in the dependency itself. This is another unrelated lockfile metadata delta that looks like a lockfile regeneration artifact; please try to keep pnpm-lock.yaml changes limited to what’s required for the new NEAR tester deps.
    pnpm-lock.yaml:25225
  • The lockfile changes any-observable@0.3.0’s peerDependencies.rxjs from '*' to '^5.5.10' (package version unchanged). Unless this is a known pnpm/registry metadata normalization, it’s unrelated churn; please keep lockfile diffs scoped to the NEAR tester addition if possible.

@sonarqubecloud

sonarqubecloud Bot commented Aug 3, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automation CI/CD stuff common Has changes in live-common shared-lib Label added for automated tagging of PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants