From 56c21dcfb38fa65c76ed577acf0f76ee30d5d9e3 Mon Sep 17 00:00:00 2001 From: rileystephens28 Date: Wed, 27 Nov 2024 11:07:54 -0600 Subject: [PATCH 1/5] Fix issue from previous rebase --- src/transaction/abstract-coinselector.ts | 25 ++++++++++++++++++++++++ src/transaction/coinselector-fewest.ts | 24 ----------------------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/transaction/abstract-coinselector.ts b/src/transaction/abstract-coinselector.ts index d825a099..e00f866d 100644 --- a/src/transaction/abstract-coinselector.ts +++ b/src/transaction/abstract-coinselector.ts @@ -41,6 +41,7 @@ export interface CoinSelectionConfig { target?: bigint; fee?: bigint; includeLocked?: boolean; + maxDenomination?: number; // Any future parameters can be added here } @@ -113,4 +114,28 @@ export abstract class AbstractCoinSelector { throw new Error('No UTXOs available'); } } + + /** + * Sorts UTXOs by their denomination. + * + * @param {UTXO[]} utxos - The UTXOs to sort. + * @param {'asc' | 'desc'} direction - The direction to sort ('asc' for ascending, 'desc' for descending). + * @returns {UTXO[]} The sorted UTXOs. + */ + protected sortUTXOsByDenomination(utxos: UTXO[], direction: 'asc' | 'desc'): UTXO[] { + if (direction === 'asc') { + return [...utxos].sort((a, b) => { + const diff = + BigInt(a.denomination !== null ? denominations[a.denomination] : 0) - + BigInt(b.denomination !== null ? denominations[b.denomination] : 0); + return diff > BigInt(0) ? 1 : diff < BigInt(0) ? -1 : 0; + }); + } + return [...utxos].sort((a, b) => { + const diff = + BigInt(b.denomination !== null ? denominations[b.denomination] : 0) - + BigInt(a.denomination !== null ? denominations[a.denomination] : 0); + return diff > BigInt(0) ? 1 : diff < BigInt(0) ? -1 : 0; + }); + } } diff --git a/src/transaction/coinselector-fewest.ts b/src/transaction/coinselector-fewest.ts index 21c12747..6e27ff82 100644 --- a/src/transaction/coinselector-fewest.ts +++ b/src/transaction/coinselector-fewest.ts @@ -331,28 +331,4 @@ export class FewestCoinSelector extends AbstractCoinSelector { this.changeOutputs = this.createChangeOutputs(changeAmount); } - - /** - * Sorts UTXOs by their denomination. - * - * @param {UTXO[]} utxos - The UTXOs to sort. - * @param {'asc' | 'desc'} direction - The direction to sort ('asc' for ascending, 'desc' for descending). - * @returns {UTXO[]} The sorted UTXOs. - */ - private sortUTXOsByDenomination(utxos: UTXO[], direction: 'asc' | 'desc'): UTXO[] { - if (direction === 'asc') { - return [...utxos].sort((a, b) => { - const diff = - BigInt(a.denomination !== null ? denominations[a.denomination] : 0) - - BigInt(b.denomination !== null ? denominations[b.denomination] : 0); - return diff > BigInt(0) ? 1 : diff < BigInt(0) ? -1 : 0; - }); - } - return [...utxos].sort((a, b) => { - const diff = - BigInt(b.denomination !== null ? denominations[b.denomination] : 0) - - BigInt(a.denomination !== null ? denominations[a.denomination] : 0); - return diff > BigInt(0) ? 1 : diff < BigInt(0) ? -1 : 0; - }); - } } From 06315f8abccb30018f55d662ecc60b629a862406 Mon Sep 17 00:00:00 2001 From: rileystephens28 Date: Wed, 27 Nov 2024 11:08:18 -0600 Subject: [PATCH 2/5] Small fixes and improvements --- src/wallet/qi-hdwallet.ts | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/src/wallet/qi-hdwallet.ts b/src/wallet/qi-hdwallet.ts index 6150e4f9..7ccf926a 100644 --- a/src/wallet/qi-hdwallet.ts +++ b/src/wallet/qi-hdwallet.ts @@ -145,7 +145,7 @@ export class QiHDWallet extends AbstractHDWallet { * @ignore * @type {number} */ - protected static _GAP_LIMIT: number = 2; + protected static _GAP_LIMIT: number = 5; /** * @ignore @@ -218,7 +218,9 @@ export class QiHDWallet extends AbstractHDWallet { * @returns {string[]} The payment codes for all open channels. */ get openChannels(): string[] { - return Array.from(this._addressesMap.keys()).filter((key) => !key.startsWith('BIP44:')); + return Array.from(this._addressesMap.keys()).filter( + (key) => !key.startsWith('BIP44:') && key !== QiHDWallet.PRIVATE_KEYS_PATH, + ); } /** @@ -286,23 +288,20 @@ export class QiHDWallet extends AbstractHDWallet { zone: Zone, isChange: boolean, ): QiAddressInfo { - // scan the wallet outpoints for the new address. If the address is found, set the status to USED - const outpointInfo = this._availableOutpoints.find((outpoint) => outpoint.address === addressNode.address); - const status = outpointInfo ? AddressStatus.USED : AddressStatus.UNUSED; - + const derivationPath = isChange ? 'BIP44:change' : 'BIP44:external'; const qiAddressInfo: QiAddressInfo = { + zone, + account, + derivationPath, address: addressNode.address, pubKey: addressNode.publicKey, - account, index: addressNode.index, change: isChange, - zone, - status, - derivationPath: isChange ? 'BIP44:change' : 'BIP44:external', + status: AddressStatus.UNKNOWN, lastSyncedBlock: null, }; - this._addressesMap.get(isChange ? 'BIP44:change' : 'BIP44:external')!.push(qiAddressInfo); // _addressesMap is initialized within the constructor + this._addressesMap.get(derivationPath)!.push(qiAddressInfo); // _addressesMap is initialized within the constructor return qiAddressInfo; } @@ -550,7 +549,6 @@ export class QiHDWallet extends AbstractHDWallet { blockNumber?: number, ): Promise { return this.getOutpoints(zone) - .filter((utxo) => utxo.zone === zone) .filter((utxo) => utxo.outpoint.lock === 0 || utxo.outpoint.lock! < blockNumber!) .reduce((total, utxo) => { const denominationValue = denominations[utxo.outpoint.denomination]; @@ -833,8 +831,6 @@ export class QiHDWallet extends AbstractHDWallet { } let attempts = 0; - let finalFee = 0n; - let satisfiedFeeEstimation = false; const MAX_FEE_ESTIMATION_ATTEMPTS = 5; while (attempts < MAX_FEE_ESTIMATION_ATTEMPTS) { @@ -845,10 +841,10 @@ export class QiHDWallet extends AbstractHDWallet { changeAddresses, ); - finalFee = await this.provider.estimateFeeForQi(feeEstimationTx); + const estimatedFee = await this.provider.estimateFeeForQi(feeEstimationTx); // Get new selection with updated fee 2x - selection = fewestCoinSelector.performSelection({ target: spendTarget, fee: finalFee * 3n }); + selection = fewestCoinSelector.performSelection({ target: spendTarget, fee: estimatedFee * 3n }); // Determine if new addresses are needed for the change outputs const changeAddressesNeeded = selection.changeOutputs.length - changeAddresses.length; @@ -890,19 +886,12 @@ export class QiHDWallet extends AbstractHDWallet { // If we need 5 or fewer new outputs, we can break the loop if ((changeAddressesNeeded <= 0 && spendAddressesNeeded <= 0) || totalNewOutputsNeeded <= 5) { - finalFee *= 3n; // Increase the fee 3x to ensure it's accepted - satisfiedFeeEstimation = true; break; } attempts++; } - // If we didn't satisfy the fee estimation, increase the fee 10x to ensure it's accepted - if (!satisfiedFeeEstimation) { - finalFee *= 10n; - } - // Proceed with creating and signing the transaction const chainId = (await this.provider.getNetwork()).chainId; const tx = await this.prepareTransaction( From 85c7a300849fbfff83ef494bb90a1c6f89dc41ac Mon Sep 17 00:00:00 2001 From: rileystephens28 Date: Wed, 27 Nov 2024 11:11:46 -0600 Subject: [PATCH 3/5] Change `getGapPaymentChannelAddresses` back to `getGapPaymentChannelAddressesForZone` --- src/wallet/qi-hdwallet.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/wallet/qi-hdwallet.ts b/src/wallet/qi-hdwallet.ts index 7ccf926a..00d7db01 100644 --- a/src/wallet/qi-hdwallet.ts +++ b/src/wallet/qi-hdwallet.ts @@ -1603,9 +1603,11 @@ export class QiHDWallet extends AbstractHDWallet { * @param {string} paymentCode - The payment code. * @returns {QiAddressInfo[]} The gap payment channel addresses for the payment code. */ - public getGapPaymentChannelAddresses(paymentCode: string): QiAddressInfo[] { + public getGapPaymentChannelAddressesForZone(paymentCode: string, zone: Zone): QiAddressInfo[] { return ( - this._addressesMap.get(paymentCode)?.filter((addressInfo) => addressInfo.status === AddressStatus.UNUSED) || + this._addressesMap + .get(paymentCode) + ?.filter((addressInfo) => addressInfo.status === AddressStatus.UNUSED && addressInfo.zone === zone) || [] ); } From fa378f92f847cbd31460df1276bd0ac09a16c55b Mon Sep 17 00:00:00 2001 From: rileystephens28 Date: Wed, 27 Nov 2024 11:56:42 -0600 Subject: [PATCH 4/5] Fix bug in address validation in deserialization process --- src/wallet/qi-hdwallet.ts | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/wallet/qi-hdwallet.ts b/src/wallet/qi-hdwallet.ts index 00d7db01..9093abc2 100644 --- a/src/wallet/qi-hdwallet.ts +++ b/src/wallet/qi-hdwallet.ts @@ -1655,6 +1655,7 @@ export class QiHDWallet extends AbstractHDWallet { */ public static async deserialize(serialized: SerializedQiHDWallet): Promise { super.validateSerializedWallet(serialized); + // create the wallet instance const mnemonic = Mnemonic.fromPhrase(serialized.phrase); const path = (this as any).parentPath(serialized.coinType); @@ -1663,18 +1664,23 @@ export class QiHDWallet extends AbstractHDWallet { // validate and import all the wallet addresses for (const addressInfo of serialized.addresses) { - wallet.validateAddressInfo(addressInfo); let key = addressInfo.derivationPath; if (isHexString(key, 32)) { key = QiHDWallet.PRIVATE_KEYS_PATH; - } else if (!key.startsWith('BIP44:')) { - wallet._addressesMap.set(key, []); + } else if (key.includes('BIP44')) { + // only validate if it's not a private key or a BIP44 path + wallet.validateAddressInfo(addressInfo); + } else { + // payment code addresses require different derivation validation + wallet.validateBaseAddressInfo(addressInfo); + wallet.validateExtendedProperties(addressInfo); } const existingAddresses = wallet._addressesMap.get(key); - if (existingAddresses && existingAddresses.some((addr) => addr.address === addressInfo.address)) { - throw new Error(`Address ${addressInfo.address} already exists in the wallet`); + if (!existingAddresses) { + wallet._addressesMap.set(key, [addressInfo]); + } else if (!existingAddresses.some((addr) => addr.address === addressInfo.address)) { + existingAddresses!.push(addressInfo); } - wallet._addressesMap.get(key)!.push(addressInfo); } // validate and import the counter party payment code info @@ -1683,10 +1689,13 @@ export class QiHDWallet extends AbstractHDWallet { throw new Error(`Invalid payment code: ${paymentCode}`); } for (const pcInfo of paymentCodeInfoArray) { - wallet.validateAddressInfo(pcInfo); + // Basic property validation + wallet.validateBaseAddressInfo(pcInfo); + wallet.validateExtendedProperties(pcInfo); } wallet._paymentCodeSendAddressMap.set(paymentCode, paymentCodeInfoArray); } + return wallet; } From 3a82e85db80e4c71e4ce44c038a430ab83868a9e Mon Sep 17 00:00:00 2001 From: rileystephens28 Date: Wed, 27 Nov 2024 12:26:56 -0600 Subject: [PATCH 5/5] Enforce standard set of fields for tx when calling estimateGas and createAccesslist --- src/signers/abstract-signer.ts | 36 ++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/signers/abstract-signer.ts b/src/signers/abstract-signer.ts index f5e627a5..38beee38 100644 --- a/src/signers/abstract-signer.ts +++ b/src/signers/abstract-signer.ts @@ -114,17 +114,6 @@ export abstract class AbstractSigner

this.provider).getNetwork(); @@ -134,6 +123,29 @@ export abstract class AbstractSigner