diff --git a/src/common/plugin/makeCurrencyTools.ts b/src/common/plugin/makeCurrencyTools.ts index e2145b57..ac46b900 100644 --- a/src/common/plugin/makeCurrencyTools.ts +++ b/src/common/plugin/makeCurrencyTools.ts @@ -16,6 +16,7 @@ import { parsePathname } from '../utxobased/engine/utils' import { asNumbWalletInfo, asPrivateKey, + getPrimaryFormat, PrivateKey } from '../utxobased/keymanager/cleaners' import { EncodeUriMetadata, ExtendedParseUri, PluginInfo } from './types' @@ -42,10 +43,11 @@ export function makeCurrencyTools( opts?: JsonObject ): Promise { const mnemonic = bip39.entropyToMnemonic(Buffer.from(io.random(32))) + const currencyFormats = engineInfo.formats ?? (['bip44'] as ['bip44']) const privateKey: PrivateKey = { seed: mnemonic, - format: opts?.format ?? engineInfo.formats?.[0] ?? 'bip44', + format: getPrimaryFormat(currencyFormats, currencyFormats), coinType: opts?.coinType ?? coinInfo.coinType ?? 0 } diff --git a/src/common/utxobased/engine/makeUtxoEngine.ts b/src/common/utxobased/engine/makeUtxoEngine.ts index 48b9d125..7e80e6bf 100644 --- a/src/common/utxobased/engine/makeUtxoEngine.ts +++ b/src/common/utxobased/engine/makeUtxoEngine.ts @@ -191,8 +191,8 @@ export async function makeUtxoEngine( }, getDisplayPublicSeed(): string | null { - const xpubs = publicKey.publicKeys - return Object.values(xpubs).join('\n') + const xpubs = Object.values(publicKey.publicKeys) + return xpubs.length > 0 ? xpubs.join('\n') : null }, async getEnabledTokens(): Promise { diff --git a/src/common/utxobased/keymanager/cleaners.ts b/src/common/utxobased/keymanager/cleaners.ts index 02bbb7cc..d0c24d9c 100644 --- a/src/common/utxobased/keymanager/cleaners.ts +++ b/src/common/utxobased/keymanager/cleaners.ts @@ -99,6 +99,31 @@ export const getSupportedFormats = ( } } +/** + * An algorithm that defines the primary format for a wallet given all of the + * wallet's formats and the currency formats (from plugin info). + * + * The purpose for this algorithm is to deterministically return a single + * primary format regardless of the order of the wallet formats given. + * The algorithm effectively takes the first format in the formats from plugin + * info that matches one of the formats in the given wallet formats. + * + * If no wallet format matches any of the formats in the plugin info, then + * the primary format is the first format in the wallet format after sorting + * in ascending order lexicographically. + */ +export const getPrimaryFormat = ( + currencyFormats: CurrencyFormat[], + walletFormats: CurrencyFormat[] +): CurrencyFormat => { + return ( + (currencyFormats.length > 0 + ? currencyFormats.find(format => walletFormats.includes(format)) + : undefined) ?? + walletFormats.sort((a, b) => (a === b ? 0 : a > b ? 1 : -1))[0] + ) +} + /** * A cleaner that desensitizes the walletInfo object, excluding sensitive * keys (seed/mnemonic, sync key, data key, etc). By using this object type @@ -147,16 +172,10 @@ export const asNumbWalletInfo = ( if (walletFormats.length === 0) { throw new Error('Missing wallet public keys') } - - // Search the engineInfo's formats array for the first format that exists - // in the publicKey data. - // If there are no defined formats in the engineInfo, then fallback to the - // first format in the publicKey after sorting alphabetically. - const primaryFormat = - (engineInfo.formats != null && engineInfo.formats.length > 0 - ? engineInfo.formats.find(format => walletFormats.includes(format)) - : undefined) ?? - walletFormats.sort((a, b) => (a === b ? 0 : a > b ? 1 : -1))[0] + const primaryFormat = getPrimaryFormat( + engineInfo.formats ?? [], + walletFormats + ) return { id, @@ -181,6 +200,8 @@ export const asNumbWalletInfo = ( id, type, keys: { + // Private key format is the primary format because it was determined + // during `createPrivateKey` phase. primaryFormat: privateKey.format, walletFormats, publicKey: { publicKeys: publicKey }