-
Notifications
You must be signed in to change notification settings - Fork 18
fix: enable P/X chain support for Keystone QR code connections #709
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
d6644bb
d259e24
bbadb7b
2754146
a55b65f
971860a
72e4997
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,7 +11,8 @@ import { FC, useState, useEffect, useCallback, useRef } from 'react'; | |
| import { getAddressPublicKeyFromXPub } from '@avalabs/core-wallets-sdk'; | ||
|
|
||
| import { useCameraPermissions } from '@core/ui'; | ||
| import { EVM_BASE_DERIVATION_PATH } from '@core/types'; | ||
| import { EVM_BASE_DERIVATION_PATH, ExtendedPublicKey } from '@core/types'; | ||
| import { getAvalancheExtendedKeyPath } from '@core/common'; | ||
|
|
||
| import { VideoFeedCrosshair } from '@/components/keystone'; | ||
|
|
||
|
|
@@ -68,6 +69,30 @@ export const KeystoneQRConnector: FC<KeystoneQRConnectorProps> = ({ | |
| [minNumberOfKeys], | ||
| ); | ||
|
|
||
| const getAvmAddressPublicKeys = useCallback( | ||
| async (extendedPublicKeyHex: string) => { | ||
| const keys: PublicKey[] = []; | ||
| const startingIndexes = Array.from( | ||
| { length: minNumberOfKeys }, | ||
| (_, i) => i, | ||
| ); | ||
| for (const index of startingIndexes) { | ||
| const avmKey = await getAddressPublicKeyFromXPub( | ||
| extendedPublicKeyHex, | ||
| index, | ||
| ); | ||
| keys.push({ | ||
| index, | ||
| vm: 'AVM', | ||
| key: buildAddressPublicKey(avmKey, index, 'AVM'), | ||
| }); | ||
| } | ||
|
|
||
| return keys; | ||
| }, | ||
| [minNumberOfKeys], | ||
| ); | ||
|
|
||
| const handleUnreadableQRCode = useCallback( | ||
| (isDimensionsError: boolean) => { | ||
| setStatus('error'); | ||
|
|
@@ -84,24 +109,46 @@ export const KeystoneQRConnector: FC<KeystoneQRConnectorProps> = ({ | |
| const cryptoMultiAccounts = CryptoMultiAccounts.fromCBOR(buffer); | ||
|
|
||
| const masterFingerprint = cryptoMultiAccounts.getMasterFingerprint(); | ||
| const [key] = cryptoMultiAccounts.getKeys(); | ||
| const allKeys = cryptoMultiAccounts.getKeys(); | ||
|
|
||
| const extendedPublicKeys: ExtendedPublicKey[] = []; | ||
| let evmAddressPublicKeys: PublicKey[] = []; | ||
| let avmAddressPublicKeys: PublicKey[] = []; | ||
|
|
||
| for (const key of allKeys) { | ||
| const path = key.getOrigin()?.getPath(); | ||
| const xpub = key.getBip32Key(); | ||
|
|
||
| if (path?.includes("44'/60'")) { | ||
| extendedPublicKeys.push( | ||
| buildExtendedPublicKey(xpub, EVM_BASE_DERIVATION_PATH), | ||
| ); | ||
| evmAddressPublicKeys = await getAddressPublicKeys(xpub); | ||
| } else if (path?.includes("44'/9000'")) { | ||
|
||
| extendedPublicKeys.push( | ||
| buildExtendedPublicKey(xpub, getAvalancheExtendedKeyPath(0)), | ||
| ); | ||
| avmAddressPublicKeys = await getAvmAddressPublicKeys(xpub); | ||
| } | ||
| } | ||
|
|
||
| if (key) { | ||
| if (extendedPublicKeys.length > 0) { | ||
| onQRCodeScanned({ | ||
| extendedPublicKeys: [ | ||
| buildExtendedPublicKey(key.getBip32Key(), EVM_BASE_DERIVATION_PATH), | ||
| ], | ||
| addressPublicKeys: await getAddressPublicKeys(key.getBip32Key()), | ||
| extendedPublicKeys, | ||
| addressPublicKeys: [...evmAddressPublicKeys, ...avmAddressPublicKeys], | ||
| masterFingerprint: masterFingerprint.toString('hex'), | ||
| }); | ||
| } else { | ||
| console.error( | ||
| '[Keystone] Invalid QR code: missing extended public key', | ||
| ); | ||
| console.error('[Keystone] Invalid QR code: no valid keys found'); | ||
| handleUnreadableQRCode(false); | ||
| } | ||
| }, | ||
| [onQRCodeScanned, getAddressPublicKeys, handleUnreadableQRCode], | ||
| [ | ||
| onQRCodeScanned, | ||
| getAddressPublicKeys, | ||
| getAvmAddressPublicKeys, | ||
| handleUnreadableQRCode, | ||
| ], | ||
| ); | ||
|
|
||
| const handleError = useCallback( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In case of EVM this would be correct - you'd be creating multiple accounts from a single XPUB (
m/44'/60'/0').In case of X/P chains, though, we want to import
minNumberOfKeysextended public keys (one for each account). What you're doing here is creating multiple (minNumberOfKeys) addresses for a single X/P account (i.e. extended public key (`m/44'/9000'/0')).In Core, the account model for X/P chains looks as follows:
m/44'/9000'/0'and from that we derive a single receive address to display in the UI (m/44'/9000'/0'/0/0).m/44'/9000'/1'and from that we derive a single receive address to display in the UI (m/44'/9000'/1'/0/0).However, if the user has funds on other addresses for a given account (e.g. has funds spread across
m/44'/9000'/0'/0/0,m/44'/9000'/0'/0/1, ``m/44'/9000'/0'/0/2`) - those funds would still be usable (given an XPUB for the account, we try to discover all addresses with activity/funds on them and make them spendable).So given an account
N, you want to import the following to make X/P chains work:m/44'/9000'/N'(to be able to find all funds under this xpub)m/44'/9000'/N'/0/0(to be able to receive funds)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in d259e24