π (signer-eth) [LIVE-25852]: Use chainId in getAddress only when checkOnDevice is enabled#1298
π (signer-eth) [LIVE-25852]: Use chainId in getAddress only when checkOnDevice is enabled#1298mbertin-ledger wants to merge 10 commits intodevelopfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
There was a problem hiding this comment.
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
GetAddressCommandAPDU encoding to include a 64-bit chainId only whencheckOnDeviceistrue(defaulting to1when omitted). - Introduce
SendGetAddressTaskand routeEthAppBinder.getAddressthrough it to optionally load/provide dynamic network context before sendinggetAddress. - Propagate/document
chainIdthrough 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.
| const effectiveChainId = | ||
| checkOnDevice && chainId !== undefined ? chainId : undefined; |
There was a problem hiding this comment.
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.
| const effectiveChainId = | |
| checkOnDevice && chainId !== undefined ? chainId : undefined; | |
| const effectiveChainId = checkOnDevice ? (chainId ?? 1) : undefined; |
| 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> = | ||
| { |
There was a problem hiding this comment.
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.
| checkOnDevice?: boolean; | ||
| returnChainCode?: boolean; | ||
| skipOpenApp?: boolean; | ||
| /** Optional chain ID for address derivation (e.g. 1 for Ethereum mainnet). When omitted, defaults to 1. */ |
There was a problem hiding this comment.
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.
| /** 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. */ |
| }, | ||
| initialValues: { | ||
| derivationPath: "44'/60'/0'/0/0", | ||
| chainId: "", |
There was a problem hiding this comment.
[COULD] be 1 by default
There was a problem hiding this comment.
Nope as we want to keep it undefined by default
| 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; |
There was a problem hiding this comment.
[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
There was a problem hiding this comment.
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.
Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
β¦NetworkContextLoader Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
50dae65 to
d72803d
Compare
π Description
This PR ensures the optional
chainIdparameter for Ethereum signergetAddressis only used whencheckOnDeviceis enabled. WhencheckOnDeviceisfalse,chainIdis ignored: it is not sent in the getAddress APDU and dynamic network context is not loaded. WhencheckOnDeviceistrue,chainIdis sent to the device for address verification and used to load dynamic network context (e.g. network name and icon); it defaults to1(Ethereum mainnet) when omitted.Changes:
checkOnDeviceis true.chainIdin options whencheckOnDeviceis true and the value is valid.AddressOptionsdocumentschainIdand the requirement that it is only used whencheckOnDeviceis true.β Context
β Checklist
checkOnDeviceis false and caller passedchainId(now ignored).π§ Checklist for the PR Reviewers