From ece311f9ab29f503f0b6bce74e2152b8f29b67dc Mon Sep 17 00:00:00 2001 From: JJ Adonis Date: Mon, 8 Jul 2024 15:36:56 +0700 Subject: [PATCH] chore(wallet): allow to specify custom account and index (#120) * chore(wallet): allow to specify custom account and index * chore(wallet): allow discovery of missing account/index * chore(wallet): allow discovery of missing account/index * chore(wallet): remove derivationPath from account as it is part of address * chore(wallet): resolved comments * chore(wallet): resolved comments --- packages/sdk/src/addresses/index.ts | 37 +++++++++++++++++------ packages/sdk/src/wallet/Ordit.ts | 46 ++++++++++++++++------------- packages/sdk/src/wallet/index.ts | 7 +++++ 3 files changed, 61 insertions(+), 29 deletions(-) diff --git a/packages/sdk/src/addresses/index.ts b/packages/sdk/src/addresses/index.ts index af8ed53f..bd6f8742 100644 --- a/packages/sdk/src/addresses/index.ts +++ b/packages/sdk/src/addresses/index.ts @@ -38,7 +38,9 @@ export function getAddressType(address: string, network: Network): AddressTypes export function getAddressesFromPublicKey( pubKey: string | Buffer, network: Network = "testnet", - format: AddressTypes | "all" = "all" + format: AddressTypes | "all" = "all", + accountIndex = 0, + addressIndex = 0 ) { if (!Buffer.isBuffer(pubKey)) { pubKey = Buffer.from(pubKey, "hex") @@ -66,7 +68,12 @@ export function getAddressesFromPublicKey( address: paymentObj.address, xkey: childNodeXOnlyPubkey.toString("hex"), format: addressTypeToName[addrType], - pub: keys.publicKey.toString("hex") + pub: keys.publicKey.toString("hex"), + derivationPath: { + account: accountIndex, + addressIndex, + path: getDerivationPath(addressTypeToName[addrType], accountIndex, addressIndex) + } }) } else { const paymentObj = createTransaction(keys.publicKey, addrType, network) @@ -74,7 +81,12 @@ export function getAddressesFromPublicKey( addresses.push({ address: paymentObj.address, format: addressTypeToName[addrType], - pub: keys.publicKey.toString("hex") + pub: keys.publicKey.toString("hex"), + derivationPath: { + account: accountIndex, + addressIndex, + path: getDerivationPath(addressTypeToName[addrType], accountIndex, addressIndex) + } }) } }) @@ -86,7 +98,12 @@ export function getAddressesFromPublicKey( address: paymentObj.address, format: addressTypeToName[format], pub: keys.publicKey.toString("hex"), - xkey: format === "p2tr" ? childNodeXOnlyPubkey.toString("hex") : undefined + xkey: format === "p2tr" ? childNodeXOnlyPubkey.toString("hex") : undefined, + derivationPath: { + account: accountIndex, + addressIndex, + path: getDerivationPath(addressTypeToName[format], accountIndex, addressIndex) + } }) } @@ -143,18 +160,20 @@ export function getAccountDataFromHdNode({ return accountData } -export function getAllAccountsFromHdNode({ hdNode, network = "testnet" }: GetAllAccountsFromHDNodeOptions) { +export function getAllAccountsFromHdNode({ hdNode, network = "testnet", account = 0, addressIndex = 0 }: GetAllAccountsFromHDNodeOptions) { const accounts: Account[] = [] const addressTypesList = Object.values(addressTypeToName) as AddressFormats[] addressTypesList.forEach((addrType) => { - const account = getAccountDataFromHdNode({ + const walletAccount = getAccountDataFromHdNode({ hdNode, format: addrType, - network + network, + account, + addressIndex }) - accounts.push(account) + accounts.push(walletAccount) }) return accounts @@ -165,6 +184,7 @@ export type Address = { xkey?: string format: string pub: string + derivationPath: Derivation } export type Derivation = { @@ -176,7 +196,6 @@ export type Derivation = { export type Account = Address & { priv: string type: AddressTypes - derivationPath: Derivation child: BIP32Interface } diff --git a/packages/sdk/src/wallet/Ordit.ts b/packages/sdk/src/wallet/Ordit.ts index dba34bd5..28b9fcb8 100644 --- a/packages/sdk/src/wallet/Ordit.ts +++ b/packages/sdk/src/wallet/Ordit.ts @@ -10,13 +10,13 @@ import ECPairFactory, { ECPairInterface } from "ecpair" import { Account, AddressFormats, - addressNameToType, + addressNameToType, DerivationIndex, getAccountDataFromHdNode, getAddressesFromPublicKey, getAllAccountsFromHdNode, getNetwork, mintFromCollection, - publishCollection, + publishCollection, SigningMessageOptions, tweakSigner } from ".." import { Network } from "../config/types" @@ -37,7 +37,7 @@ export class Ordit { selectedAddressType: AddressFormats | undefined selectedAddress: string | undefined - constructor({ wif, seed, privateKey, bip39, network = "testnet", type = "legacy" }: WalletOptions) { + constructor({ wif, seed, privateKey, bip39, network = "testnet", type = "legacy", account = 0, addressIndex = 0 }: WalletOptions) { this.#network = network const networkObj = getNetwork(network) const format = addressNameToType[type] @@ -48,7 +48,7 @@ export class Ordit { this.publicKey = keyPair.publicKey.toString("hex") - const accounts = getAddressesFromPublicKey(keyPair.publicKey, network, format) + const accounts = getAddressesFromPublicKey(keyPair.publicKey, network, format, account, addressIndex) this.#initialize(accounts) } else if (privateKey) { const pkBuffer = Buffer.from(privateKey, "hex") @@ -57,7 +57,7 @@ export class Ordit { this.publicKey = keyPair.publicKey.toString("hex") - const accounts = getAddressesFromPublicKey(keyPair.publicKey, network, format) + const accounts = getAddressesFromPublicKey(keyPair.publicKey, network, format, account, addressIndex) this.#initialize(accounts) } else if (seed) { const seedBuffer = Buffer.from(seed, "hex") @@ -65,7 +65,7 @@ export class Ordit { this.#hdNode = hdNode - const accounts = getAllAccountsFromHdNode({ hdNode, network }) + const accounts = getAllAccountsFromHdNode({ hdNode, network, account, addressIndex }) const pkBuf = Buffer.from(accounts[0].priv, "hex") this.#keyPair = ECPair.fromPrivateKey(pkBuf, { network: networkObj }) @@ -79,7 +79,7 @@ export class Ordit { this.#hdNode = hdNode - const accounts = getAllAccountsFromHdNode({ hdNode, network }) + const accounts = getAllAccountsFromHdNode({ hdNode, network, account, addressIndex }) this.#keyPair = accounts[0].child this.publicKey = this.#keyPair.publicKey.toString("hex") @@ -98,17 +98,12 @@ export class Ordit { this.#network = value } - getAddressByType(type: AddressFormats) { + getAddressByType(type: AddressFormats, derivationIndex: DerivationIndex) { if (!this.#initialized || !this.allAddresses.length) { throw new OrditSDKError("Wallet not fully initialized.") } - const result = this.allAddresses.filter((address) => address.format === type) - - if (!result) { - throw new OrditSDKError(`Address of type ${type} not found in the instance.`) - } - - return result + return this.allAddresses.find((address) => address.format === type && address.derivationPath.account === derivationIndex.accountIndex + && address.derivationPath.addressIndex === derivationIndex.addressIndex); } getAllAddresses() { @@ -119,11 +114,18 @@ export class Ordit { return this.allAddresses } - setDefaultAddress(type: AddressFormats, index = 0) { + setDefaultAddress(type: AddressFormats, { accountIndex = 0, addressIndex = 0 }: DerivationIndex) { if (this.selectedAddressType === type) return + let addressToSelect: Account; - const result = this.getAddressByType(type) as Account[] - const addressToSelect = result[index] + const account = this.getAddressByType(type, { accountIndex, addressIndex }) as Account + if (!account) { + addressToSelect = this.generateAddress(type, accountIndex, addressIndex); + // Push to current list of addresses + this.allAddresses.push(addressToSelect); + } else { + addressToSelect = account; + } if (!addressToSelect) throw new OrditSDKError("Address not found. Please add an address with the type and try again.") @@ -233,9 +235,11 @@ export class Ordit { return psbt.toHex() } - signMessage(message: string, type?: AddressFormats) { + signMessage(message: string, type?: AddressFormats, opts?: SigningMessageOptions) { const addressType = type || this.selectedAddressType - const node = this.allAddresses.find((wallet) => wallet.format === addressType) as Account + const accountIndexToSign: number = opts?.accountIndex === undefined ? 0 : opts?.accountIndex; + const addressIndexToSign: number = opts?.addressIndex === undefined ? 0 : opts?.addressIndex; + const node = this.getAddressByType(addressType!, { accountIndex: accountIndexToSign, addressIndex: addressIndexToSign }) as Account const signature = AddressUtils.isP2PKH(node.address!) ? sign(message, node.child.privateKey!) : Signer.sign(node.child.toWIF(), node.address!, message, getNetwork(this.#network)) @@ -266,6 +270,8 @@ export type WalletOptions = { bip39?: string network?: Network type?: AddressFormats + account?: number; + addressIndex?: number; } export type Address = ReturnType[0] diff --git a/packages/sdk/src/wallet/index.ts b/packages/sdk/src/wallet/index.ts index 8006dcf6..cd2f82a2 100644 --- a/packages/sdk/src/wallet/index.ts +++ b/packages/sdk/src/wallet/index.ts @@ -9,3 +9,10 @@ export type GetWalletOptions = { format: AddressTypes | "all" safeMode?: OnOffUnion } + +export interface DerivationIndex { + accountIndex: number + addressIndex: number +} + +export type SigningMessageOptions = Partial \ No newline at end of file