Skip to content

πŸ› (signer-eth) [LIVE-25852]: Use chainId in getAddress only when checkOnDevice is enabled#1298

Open
mbertin-ledger wants to merge 10 commits intodevelopfrom
bugfix/LIVE-25852-add-chain-id-eth-getaddress
Open

πŸ› (signer-eth) [LIVE-25852]: Use chainId in getAddress only when checkOnDevice is enabled#1298
mbertin-ledger wants to merge 10 commits intodevelopfrom
bugfix/LIVE-25852-add-chain-id-eth-getaddress

Conversation

@mbertin-ledger
Copy link
Member

πŸ“ Description

This PR ensures the optional chainId parameter for Ethereum signer getAddress is only used when checkOnDevice is enabled. When checkOnDevice is false, chainId is ignored: it is not sent in the getAddress APDU and dynamic network context is not loaded. When checkOnDevice is true, chainId is sent to the device for address verification and used to load dynamic network context (e.g. network name and icon); it defaults to 1 (Ethereum mainnet) when omitted.

Changes:

  • GetAddressCommand: Chain ID is included in the APDU data only when checkOnDevice is true.
  • SendGetAddressTask: Uses an effective chainId (chainId when checkOnDevice is true, undefined otherwise) for loading dynamic network context and for the command.
  • Sample app: Get address form only passes chainId in options when checkOnDevice is true and the value is valid.
  • Docs: AddressOptions documents chainId and the requirement that it is only used when checkOnDevice is true.

❓ Context

  • JIRA or GitHub link: LIVE-25852
  • Feature: getAddress chainId with checkOnDevice

βœ… Checklist

  • Covered by automatic tests (GetAddressCommand and SendGetAddressTask tests updated/added)
  • Changeset is provided
  • Documentation is up-to-date (eth signer reference updated)
  • Impact of the changes:
    • Ethereum getAddress: behavior change only when checkOnDevice is false and caller passed chainId (now ignored).
    • Sample app: chainId field only affects behavior when "Check on device" is enabled.
    • QA: verify getAddress with checkOnDevice on/off and with/without chainId on a device.

🧐 Checklist for the PR Reviewers

  • The code aligns with the requirements described in the linked JIRA or GitHub issue.
  • The PR description clearly documents the changes made and explains any technical trade-offs or design decisions.
  • There are no undocumented trade-offs, technical debt, or maintainability issues.
  • The PR has been tested thoroughly, and any potential edge cases have been considered and handled.
  • Any new dependencies have been justified and documented.

@vercel
Copy link

vercel bot commented Feb 11, 2026

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

Project Deployment Actions Updated (UTC)
device-sdk-ts-sample Ready Ready Preview, Comment Feb 11, 2026 3:48pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
doc-device-management-kit Ignored Ignored Feb 11, 2026 3:48pm

Request Review

@mbertin-ledger mbertin-ledger marked this pull request as ready for review February 11, 2026 14:00
@mbertin-ledger mbertin-ledger requested a review from a team as a code owner February 11, 2026 14:00
Copilot AI review requested due to automatic review settings February 11, 2026 14:00
@github-actions
Copy link
Contributor

github-actions bot commented Feb 11, 2026

Danger Check Results

Messages

βœ…

Danger: All checks passed successfully! πŸŽ‰

Generated by 🚫 dangerJS against d72803d

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the Ethereum signer getAddress flow so chainId is only considered when checkOnDevice is enabled, including conditional APDU encoding and conditional dynamic network context loading.

Changes:

  • Update GetAddressCommand APDU encoding to include a 64-bit chainId only when checkOnDevice is true (defaulting to 1 when omitted).
  • Introduce SendGetAddressTask and route EthAppBinder.getAddress through it to optionally load/provide dynamic network context before sending getAddress.
  • Propagate/document chainId through API types, sample app, and docs.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/signer/signer-eth/src/internal/app-binder/task/SendGetAddressTask.ts New task that optionally loads dynamic network context and then sends GetAddressCommand.
packages/signer/signer-eth/src/internal/app-binder/task/SendGetAddressTask.test.ts Adds unit tests for the new task behavior around context loading and command sending.
packages/signer/signer-eth/src/internal/app-binder/command/GetAddressCommand.ts Encodes chainId (uint64) only when checkOnDevice is enabled, with defaulting behavior.
packages/signer/signer-eth/src/internal/app-binder/command/GetAddressCommand.test.ts Updates APDU fixtures/expectations and adds chainId-specific tests.
packages/signer/signer-eth/src/internal/app-binder/EthAppBinder.ts Switches getAddress to execute a task (CallTaskInAppDeviceAction) instead of sending the command directly.
packages/signer/signer-eth/src/internal/app-binder/EthAppBinder.test.ts Updates binder tests to match the new task-based device action shape.
packages/signer/signer-eth/src/internal/address/use-case/GetAddressUseCase.ts Propagates chainId from public options down into the binder call.
packages/signer/signer-eth/src/api/model/AddressOptions.ts Documents the new chainId option on the public options type.
packages/signer/signer-eth/src/api/app-binder/GetAddressCommandTypes.ts Adds chainId?: number to the command args type.
packages/signer/context-module/src/dynamic-network/domain/DynamicNetworkContextLoader.ts Exports DYNAMIC_NETWORK_CONTEXT_TYPES for consumers to request the correct context types.
packages/device-management-kit/src/api/apdu/utils/ApduBuilder.ts Adds add64BitUIntToData helper used for chainId encoding.
apps/sample/src/components/SignerEthView/index.tsx Only forwards chainId to getAddress when checkOnDevice is enabled and input is parseable.
apps/docs/pages/docs/references/signers/eth.mdx Documents chainId and clarifies it is only used when checkOnDevice is true.
.changeset/slow-suits-relax.md Declares a patch release note for the behavior change.

πŸ’‘ Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +51 to +52
const effectiveChainId =
checkOnDevice && chainId !== undefined ? chainId : undefined;
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

effectiveChainId is only set when chainId is explicitly provided. When checkOnDevice is true and chainId is omitted, GetAddressCommand will still default and send chainId=1, but this task won’t load dynamic network context for mainnet (and also won’t pass chainId to the command args), which contradicts the documented behavior. Compute an effective chain id as checkOnDevice ? (chainId ?? 1) : undefined and use it both for context loading and for the command arguments.

Suggested change
const effectiveChainId =
checkOnDevice && chainId !== undefined ? chainId : undefined;
const effectiveChainId = checkOnDevice ? (chainId ?? 1) : undefined;

Copilot uses AI. Check for mistakes.
Comment on lines +90 to +116
it("should send GetAddressCommand with correct args (checkOnDevice, returnChainCode, chainId)", async () => {
contextModuleMock.getContexts.mockResolvedValue([]);

const task = new SendGetAddressTask(apiMock, {
...baseArgs,
checkOnDevice: true,
returnChainCode: true,
chainId: 137,
});
await task.run();

expect(apiMock.sendCommand).toHaveBeenLastCalledWith(
expect.objectContaining({
name: "getAddress",
args: expect.objectContaining({
derivationPath: DEFAULT_DERIVATION_PATH,
checkOnDevice: true,
returnChainCode: true,
chainId: 137,
}),
}),
);
});

it("should load dynamic network context and provide contexts when chainId is defined and checkOnDevice is true", async () => {
const dynamicNetworkContext: ClearSignContextSuccess<ClearSignContextType.DYNAMIC_NETWORK> =
{
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

Missing test case for checkOnDevice: true with chainId omitted. Given GetAddressCommand defaults chainId to 1, the task should also load dynamic network context with chainId=1 and send the command with chainId=1; add an assertion for contextModule.getContexts/api.getDeviceModel calls in that scenario.

Copilot uses AI. Check for mistakes.
checkOnDevice?: boolean;
returnChainCode?: boolean;
skipOpenApp?: boolean;
/** Optional chain ID for address derivation (e.g. 1 for Ethereum mainnet). When omitted, defaults to 1. */
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

The new JSDoc for chainId is misleading/inaccurate: chainId does not affect β€œaddress derivation”, and it is ignored when checkOnDevice is false. Update the comment to state it’s only used for on-device verification (and dynamic network context) when checkOnDevice is true, and only defaults to 1 in that mode.

Suggested change
/** Optional chain ID for address derivation (e.g. 1 for Ethereum mainnet). When omitted, defaults to 1. */
/** Optional chain ID used only for on-device address verification and dynamic network context when `checkOnDevice` is true (e.g. 1 for Ethereum mainnet). When omitted in that mode, defaults to 1. */

Copilot uses AI. Check for mistakes.
},
initialValues: {
derivationPath: "44'/60'/0'/0/0",
chainId: "",
Copy link
Contributor

Choose a reason for hiding this comment

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

[COULD] be 1 by default

Copy link
Member Author

Choose a reason for hiding this comment

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

Nope as we want to keep it undefined by default

Copy link
Member Author

Choose a reason for hiding this comment

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

so => [WONT DO]

builder.add32BitUIntToData(1);
// Chain ID is only sent when checkOnDevice is true; default to 1 (Ethereum mainnet) when omitted.
if (this.args.checkOnDevice) {
const chainId = this.args.chainId ?? 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

[COULD] The set to default value 1 could be in packages/signer/signer-eth/src/internal/address/use-case/GetAddressUseCase.ts like with other values: https://github.com/LedgerHQ/device-sdk-ts/pull/1298/changes#diff-c6696bcd6dc71e14305689964fec360ba66258ae46bec83dd3b1554c3e71d508R25

Copy link
Member Author

Choose a reason for hiding this comment

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

Nope because in command this has no impact on dynamic network call.
If we are putting 1, the tasks will ask for dynamic network and we should not as it's optional.

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.

2 participants