Skip to content

Conversation

@mathieuartu
Copy link
Contributor

@mathieuartu mathieuartu commented Dec 10, 2025

Related to: https://consensyssoftware.atlassian.net/browse/MUL-1320

Examples


Note

Adds QrKeyringV2 adapter around the legacy QrKeyring, exposes BIP-44 accounts via Keyring V2, exports it, adds derivation-path lookup, and introduces comprehensive tests and wiring updates.

  • Keyring V2 Adapter
    • Add QrKeyringV2 implementing KeyringV2, extending EthKeyringWrapper, exposing EOA BIP-44 accounts and methods (SignTransaction, PersonalSign, SignTypedDataV4).
    • Supports capabilities: scopes: [Eoa], bip44.deriveIndex: true.
    • Handles account listing, creation via bip44:derive-index (with entropy source validation), deletion, serialization.
  • Legacy QR Keyring Enhancements
    • Add QrKeyring#getPathFromAddress(address) to retrieve derivation paths used by the adapter.
  • Exports & Docs
    • Export QrKeyringV2 and QrKeyringV2Options from src/index.ts.
    • Update CHANGELOG.md with the new adapter.
    • Update README dependency graph to link eth-qr-keyring to keyring_api and account_api.
  • Tests
    • New qr-keyring-v2.test.ts covering constructor/capabilities, getAccounts, deserialize, createAccounts, deleteAccount, getAccount, serialize, and Account mode behavior.
  • Build/Config
    • Add @metamask/keyring-api and @metamask/account-api deps and TS project references; update yarn.lock.

Written by Cursor Bugbot for commit aa6f016. This will update automatically on new commits. Configure here.

@mathieuartu mathieuartu self-assigned this Dec 10, 2025
@mathieuartu mathieuartu changed the title feat: Add QrKeyringV2 wrapper feat(keyring-eth-qr): Add QrKeyringV2 wrapper Dec 10, 2025
@mathieuartu mathieuartu changed the title feat(keyring-eth-qr): Add QrKeyringV2 wrapper feat(keyring-eth-qr): add QrKeyringV2 wrapper Dec 10, 2025
@mathieuartu mathieuartu changed the title feat(keyring-eth-qr): add QrKeyringV2 wrapper feat: add QrKeyringV2 wrapper Dec 10, 2025
@mathieuartu mathieuartu marked this pull request as ready for review December 10, 2025 14:33
@mathieuartu mathieuartu requested a review from a team as a code owner December 10, 2025 14:33
entropy: {
type: KeyringAccountEntropyTypeOption.Mnemonic,
id: this.entropySource,
groupIndex: addressIndex,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the addressIndex only make sense for DeviceMode.HD? Since for DeviceMode.ACCOUNT we're defaulting to the arrayIndex, this won't really match the real account/address index used during the derivation?

This might also be pretty hard to go from derivation path -> index if the "QR device" can use any arbitrary path. So I wonder if DeviceMode.ACCOUNT should be treated as private key here? 🤔

NOTE: I might have misunderstood the legacy QR keyring, so correct me if I'm wrong 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm a bit confused here as well. I'll set up something internally so we can share knowledge around this area 😅

Comment on lines +189 to +192
const currentAccounts = await this.getAccounts();
const existingAccount = currentAccounts.find(
(account) => account.options.entropy.groupIndex === targetIndex,
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Following-up my previous questions, we would have to consider only BIP-44 accounts in this case.

Comment on lines +198 to +199
this.inner.setAccountToUnlock(targetIndex);
const [newAddress] = await this.inner.addAccounts(1);
Copy link
Contributor

Choose a reason for hiding this comment

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

Question: (Might be a bit too much) but, I wonder if we should reset the #accountToUnlock back to what it was here? Like if for some reason the previous value had to be kept, then, our implementation side effect could have an impact on that? 🤔

Basically I'm thinking about something like:

const previousAccountToUnlock = this.inner.getAccountToUnlock();
let newAddress;
try {
  this.inner.setAccountToUnlock(targetIndex);
  [newAddress] = await this.inner.addAccounts(1);
} finally {
  this.inner.setAccountToUnlock(previousAccountToUnlock);
}

But even this, is not entirely concurrent-safe (if another addAccounts happens in parallel 🙃).

Copy link
Contributor

Choose a reason for hiding this comment

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

We can discuss this internally, but I think the legacy implementation is a bit fragile regarding this logic anyway, like, if you use setAccountToUnlock(n) and addAccounts(2), this will try to create the same account twice (since #accountToUnlock is not cleared after creating an account 😅)

The legacy logic should probably be fixed to only accept addAccounts(1) if #accountToUnlock is set!

But our wrapper implementation is good otherwise!

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