From a16f57ac0d745a2daa2df0f9431c317ac22565c0 Mon Sep 17 00:00:00 2001 From: robertlincecum Date: Thu, 13 Jun 2024 09:56:33 -0500 Subject: [PATCH 1/2] make from not required so static contract calls work --- src/contract/contract.ts | 17 +- src/providers/provider.ts | 5 +- src/transaction/quai-transaction.ts | 23 +- src/wallet/hdwallet.ts | 161 ++++++++------ src/wallet/qi-hdwallet.ts | 318 ++++++++++++++-------------- src/wallet/quai-hdwallet.ts | 9 +- 6 files changed, 278 insertions(+), 255 deletions(-) diff --git a/src/contract/contract.ts b/src/contract/contract.ts index ce1df835..d049a02a 100644 --- a/src/contract/contract.ts +++ b/src/contract/contract.ts @@ -346,7 +346,7 @@ function buildWrappedMethod< return await resolveProperties({ to: contract.getAddress(), - from: args.pop()?.from ?? '0x0000000000000000000000000000000000000000', + from: args.pop()?.from, data: contract.interface.encodeFunctionData(fragment, resolvedArgs), }); }; @@ -365,12 +365,7 @@ function buildWrappedMethod< operation: 'sendTransaction', }); const pop = await populateTransaction(...args); - if ( - runner && - 'address' in runner && - typeof runner.address == 'string' && - pop.from === '0x0000000000000000000000000000000000000000' - ) { + if (!pop.from && 'address' in runner && typeof runner.address === 'string') { pop.from = runner.address; } @@ -396,14 +391,6 @@ function buildWrappedMethod< operation: 'call', }); const tx = await populateTransaction(...args); - if ( - runner && - 'address' in runner && - typeof runner.address == 'string' && - tx.from === '0x0000000000000000000000000000000000000000' - ) { - tx.from = runner.address; - } let result = '0x'; try { diff --git a/src/providers/provider.ts b/src/providers/provider.ts index b7785387..2bbe35cc 100644 --- a/src/providers/provider.ts +++ b/src/providers/provider.ts @@ -139,7 +139,10 @@ export function addressFromTransactionRequest(tx: TransactionRequest): AddressLi keccak256('0x' + SigningKey.computePublicKey(tx.inputs[0].pub_key).substring(4)).substring(26), ); } - throw new Error('Unable to determine sender from transaction inputs or from field'); + if ('to' in tx && tx.to !== null) { + return tx.to as AddressLike; + } + throw new Error('Unable to determine address from transaction inputs, from or to field'); } /** * A **TransactionRequest** is a transactions with potentially various properties not defined, or with less strict types diff --git a/src/transaction/quai-transaction.ts b/src/transaction/quai-transaction.ts index 2d244b5f..1367eb9d 100644 --- a/src/transaction/quai-transaction.ts +++ b/src/transaction/quai-transaction.ts @@ -34,7 +34,7 @@ export interface QuaiTransactionLike extends TransactionLike { /** * The sender. */ - from: string; + from?: string; /** * The nonce. */ @@ -107,7 +107,7 @@ export class QuaiTransaction extends AbstractTransaction implements Q #maxFeePerGas: null | bigint; #value: bigint; #accessList: null | AccessList; - from: string; + from?: string; /** * The `to` address for the transaction or `null` if the transaction is an `init` transaction. @@ -131,7 +131,7 @@ export class QuaiTransaction extends AbstractTransaction implements Q throw new Error('Invalid Zone for from address'); } - const isSameLedger = isQiAddress(this.from) === isQiAddress(this.to || ''); + const isSameLedger = !(this.from && this.to) || isQiAddress(this.from) === isQiAddress(this.to); if (this.isExternal && !isSameLedger) { throw new Error('Cross-zone & cross-ledger transactions are not supported'); } @@ -142,7 +142,11 @@ export class QuaiTransaction extends AbstractTransaction implements Q const hashHex = keccak256(dataBuffer); const hashBuffer = Buffer.from(hashHex.substring(2), 'hex'); - const origin = this.originZone ? parseInt(this.originZone.slice(2), 16) : 0; + const origin = this.originZone + ? parseInt(this.originZone.slice(2), 16) + : this.destZone + ? parseInt(this.destZone.slice(2), 16) + : 0; hashBuffer[0] = origin; hashBuffer[1] &= 0x7f; hashBuffer[2] = origin; @@ -155,7 +159,7 @@ export class QuaiTransaction extends AbstractTransaction implements Q * The zone of the sender address */ get originZone(): Zone | undefined { - const zone = getZoneForAddress(this.from); + const zone = this.from ? getZoneForAddress(this.from) : undefined; return zone ?? undefined; } @@ -268,7 +272,7 @@ export class QuaiTransaction extends AbstractTransaction implements Q /** * Creates a new Transaction with default values. */ - constructor(from: string) { + constructor(from?: string) { super(); this.#to = null; this.#nonce = 0; @@ -441,7 +445,12 @@ export class QuaiTransaction extends AbstractTransaction implements Q if (tx.from != null) { validateAddress(tx.from); - assertArgument(result.from.toLowerCase() === (tx.from || '').toLowerCase(), 'from mismatch', 'tx', tx); + assertArgument( + (result.from || '').toLowerCase() === (tx.from || '').toLowerCase(), + 'from mismatch', + 'tx', + tx, + ); result.from = tx.from; } return result; diff --git a/src/wallet/hdwallet.ts b/src/wallet/hdwallet.ts index acc73b88..69e2f499 100644 --- a/src/wallet/hdwallet.ts +++ b/src/wallet/hdwallet.ts @@ -1,27 +1,27 @@ -import { HDNodeWallet } from "./hdnodewallet"; -import { Mnemonic } from "./mnemonic.js"; -import { LangEn } from "../wordlists/lang-en.js" -import type { Wordlist } from "../wordlists/index.js"; -import { randomBytes } from "../crypto/index.js"; -import { getZoneForAddress, isQiAddress } from "../utils/index.js"; +import { HDNodeWallet } from './hdnodewallet.js'; +import { Mnemonic } from './mnemonic.js'; +import { LangEn } from '../wordlists/lang-en.js'; +import type { Wordlist } from '../wordlists/index.js'; +import { randomBytes } from '../crypto/index.js'; +import { getZoneForAddress, isQiAddress } from '../utils/index.js'; import { Zone } from '../constants/index.js'; import { TransactionRequest, Provider, TransactionResponse } from '../providers/index.js'; -import { AllowedCoinType } from "../constants/index.js"; +import { AllowedCoinType } from '../constants/index.js'; export interface NeuteredAddressInfo { - pubKey: string; - address: string; - account: number; - index: number; - change: boolean; - zone: Zone; + pubKey: string; + address: string; + account: number; + index: number; + change: boolean; + zone: Zone; } // Constant to represent the maximum attempt to derive an address const MAX_ADDRESS_DERIVATION_ATTEMPTS = 10000000; export abstract class AbstractHDWallet { - protected static _coinType?: AllowedCoinType; + protected static _coinType?: AllowedCoinType; // Map of account number to HDNodeWallet protected _accounts: Map = new Map(); @@ -32,7 +32,7 @@ export abstract class AbstractHDWallet { // Root node of the HD wallet protected _root: HDNodeWallet; - protected provider?: Provider; + protected provider?: Provider; /** * @private @@ -43,12 +43,12 @@ export abstract class AbstractHDWallet { } protected static parentPath(coinType: number): string { - return `m/44'/${coinType}'`; - } - - protected coinType(): number { - return (this.constructor as typeof AbstractHDWallet)._coinType!; - } + return `m/44'/${coinType}'`; + } + + protected coinType(): number { + return (this.constructor as typeof AbstractHDWallet)._coinType!; + } // helper methods that adds an account HD node to the HD wallet following the BIP-44 standard. protected addAccount(accountIndex: number): void { @@ -56,31 +56,38 @@ export abstract class AbstractHDWallet { this._accounts.set(accountIndex, newNode); } - protected deriveAddress(account: number, startingIndex: number, zone: Zone, isChange: boolean = false): HDNodeWallet { + protected deriveAddress( + account: number, + startingIndex: number, + zone: Zone, + isChange: boolean = false, + ): HDNodeWallet { this.validateZone(zone); const isValidAddressForZone = (address: string) => { const addressZone = getZoneForAddress(address); if (!addressZone) { return false; } - const isCorrectShard = addressZone === zone; - const isCorrectLedger = (this.coinType() === 969) ? isQiAddress(address) : !isQiAddress(address); - return isCorrectShard && isCorrectLedger; - } - // derive the address node - const accountNode = this._accounts.get(account); - const changeIndex = isChange ? 1 : 0; - const changeNode = accountNode!.deriveChild(changeIndex); - let addrIndex: number = startingIndex; - let addressNode: HDNodeWallet; - do { - addressNode = changeNode.deriveChild(addrIndex); - addrIndex++; - // put a hard limit on the number of addresses to derive - if (addrIndex - startingIndex > MAX_ADDRESS_DERIVATION_ATTEMPTS) { - throw new Error(`Failed to derive a valid address for the zone ${zone} after ${MAX_ADDRESS_DERIVATION_ATTEMPTS} attempts.`); - } - } while (!isValidAddressForZone(addressNode.address)); + const isCorrectShard = addressZone === zone; + const isCorrectLedger = this.coinType() === 969 ? isQiAddress(address) : !isQiAddress(address); + return isCorrectShard && isCorrectLedger; + }; + // derive the address node + const accountNode = this._accounts.get(account); + const changeIndex = isChange ? 1 : 0; + const changeNode = accountNode!.deriveChild(changeIndex); + let addrIndex: number = startingIndex; + let addressNode: HDNodeWallet; + do { + addressNode = changeNode.deriveChild(addrIndex); + addrIndex++; + // put a hard limit on the number of addresses to derive + if (addrIndex - startingIndex > MAX_ADDRESS_DERIVATION_ATTEMPTS) { + throw new Error( + `Failed to derive a valid address for the zone ${zone} after ${MAX_ADDRESS_DERIVATION_ATTEMPTS} attempts.`, + ); + } + } while (!isValidAddressForZone(addressNode.address)); return addressNode; } @@ -112,7 +119,7 @@ export abstract class AbstractHDWallet { return neuteredAddressInfo; } - public getNextAddress(accountIndex: number, zone: Zone): NeuteredAddressInfo { + public getNextAddress(accountIndex: number, zone: Zone): NeuteredAddressInfo { this.validateZone(zone); if (!this._accounts.has(accountIndex)) { this.addAccount(accountIndex); @@ -154,35 +161,55 @@ export abstract class AbstractHDWallet { return Array.from(addresses).filter((addressInfo) => addressInfo.account === account); } - public getAddressesForZone(zone: Zone): NeuteredAddressInfo[] { + public getAddressesForZone(zone: Zone): NeuteredAddressInfo[] { this.validateZone(zone); - const addresses = this._addresses.values(); - return Array.from(addresses).filter((addressInfo) => addressInfo.zone === zone); - } + const addresses = this._addresses.values(); + return Array.from(addresses).filter((addressInfo) => addressInfo.zone === zone); + } - protected static createInstance(this: new (root: HDNodeWallet) => T, mnemonic: Mnemonic): T { + protected static createInstance( + this: new (root: HDNodeWallet) => T, + mnemonic: Mnemonic, + ): T { const coinType = (this as any)._coinType; - const root = HDNodeWallet.fromMnemonic(mnemonic, (this as any).parentPath(coinType)); - return new this(root); - } - - static fromMnemonic(this: new (root: HDNodeWallet) => T, mnemonic: Mnemonic): T { - return (this as any).createInstance(mnemonic); - } - - static createRandom(this: new (root: HDNodeWallet) => T, password?: string, wordlist?: Wordlist): T { - if (password == null) { password = ""; } - if (wordlist == null) { wordlist = LangEn.wordlist(); } - const mnemonic = Mnemonic.fromEntropy(randomBytes(16), password, wordlist); - return (this as any).createInstance(mnemonic); - } - - static fromPhrase(this: new (root: HDNodeWallet) => T, phrase: string, password?: string, wordlist?: Wordlist): T { - if (password == null) { password = ""; } - if (wordlist == null) { wordlist = LangEn.wordlist(); } - const mnemonic = Mnemonic.fromPhrase(phrase, password, wordlist); - return (this as any).createInstance(mnemonic); - } + const root = HDNodeWallet.fromMnemonic(mnemonic, (this as any).parentPath(coinType)); + return new this(root); + } + + static fromMnemonic(this: new (root: HDNodeWallet) => T, mnemonic: Mnemonic): T { + return (this as any).createInstance(mnemonic); + } + + static createRandom( + this: new (root: HDNodeWallet) => T, + password?: string, + wordlist?: Wordlist, + ): T { + if (password == null) { + password = ''; + } + if (wordlist == null) { + wordlist = LangEn.wordlist(); + } + const mnemonic = Mnemonic.fromEntropy(randomBytes(16), password, wordlist); + return (this as any).createInstance(mnemonic); + } + + static fromPhrase( + this: new (root: HDNodeWallet) => T, + phrase: string, + password?: string, + wordlist?: Wordlist, + ): T { + if (password == null) { + password = ''; + } + if (wordlist == null) { + wordlist = LangEn.wordlist(); + } + const mnemonic = Mnemonic.fromPhrase(phrase, password, wordlist); + return (this as any).createInstance(mnemonic); + } abstract signTransaction(tx: TransactionRequest): Promise; diff --git a/src/wallet/qi-hdwallet.ts b/src/wallet/qi-hdwallet.ts index 8a7f88e2..03b37197 100644 --- a/src/wallet/qi-hdwallet.ts +++ b/src/wallet/qi-hdwallet.ts @@ -1,7 +1,5 @@ - - -import { AbstractHDWallet, NeuteredAddressInfo } from './hdwallet'; -import { HDNodeWallet } from "./hdnodewallet"; +import { AbstractHDWallet, NeuteredAddressInfo } from './hdwallet.js'; +import { HDNodeWallet } from './hdnodewallet.js'; import { QiTransactionRequest, Provider, TransactionResponse } from '../providers/index.js'; import { computeAddress } from '../address/index.js'; import { getBytes, hexlify } from '../utils/index.js'; @@ -22,37 +20,39 @@ type OutpointInfo = { }; export class QiHDWallet extends AbstractHDWallet { - protected static _GAP_LIMIT: number = 20; protected static _coinType: AllowedCoinType = 969; - // Map of change addresses to address info - protected _changeAddresses: Map = new Map(); + // Map of change addresses to address info + protected _changeAddresses: Map = new Map(); - // Array of gap addresses - protected _gapChangeAddresses: NeuteredAddressInfo[] = []; + // Array of gap addresses + protected _gapChangeAddresses: NeuteredAddressInfo[] = []; - // Array of gap change addresses - protected _gapAddresses: NeuteredAddressInfo[] = []; - - protected _outpoints: OutpointInfo[] = []; + // Array of gap change addresses + protected _gapAddresses: NeuteredAddressInfo[] = []; + + protected _outpoints: OutpointInfo[] = []; private constructor(root: HDNodeWallet, provider?: Provider) { super(root, provider); } - public getNextChangeAddress(account: number, zone: Zone): NeuteredAddressInfo { - this.validateZone(zone); - if (!this._accounts.has(account)) { - this.addAccount(account); - } - const filteredAccountInfos = Array.from(this._changeAddresses.values()).filter((addressInfo) => - addressInfo.account === account && addressInfo.zone === zone - ); - const lastIndex = filteredAccountInfos.reduce((maxIndex, addressInfo) => Math.max(maxIndex, addressInfo.index), -1); - // call derive address with change = true - const addressNode = this.deriveAddress(account, lastIndex + 1, zone, true); + public getNextChangeAddress(account: number, zone: Zone): NeuteredAddressInfo { + this.validateZone(zone); + if (!this._accounts.has(account)) { + this.addAccount(account); + } + const filteredAccountInfos = Array.from(this._changeAddresses.values()).filter( + (addressInfo) => addressInfo.account === account && addressInfo.zone === zone, + ); + const lastIndex = filteredAccountInfos.reduce( + (maxIndex, addressInfo) => Math.max(maxIndex, addressInfo.index), + -1, + ); + // call derive address with change = true + const addressNode = this.deriveAddress(account, lastIndex + 1, zone, true); const neuteredAddressInfo = { pubKey: addressNode.publicKey, @@ -68,31 +68,30 @@ export class QiHDWallet extends AbstractHDWallet { return neuteredAddressInfo; } - public importOutpoints(outpoints: OutpointInfo[]): void { + public importOutpoints(outpoints: OutpointInfo[]): void { outpoints.forEach((outpoint) => { this.validateZone(outpoint.zone); this._outpoints.push(outpoint); }); - - } - - public getOutpoints(zone: Zone): OutpointInfo[] { - this.validateZone(zone); - return this._outpoints.filter((outpoint) => outpoint.zone === zone); - } - - /** - * Signs a Qi transaction and returns the serialized transaction - * - * @param {QiTransactionRequest} tx - The transaction to sign. - * - * @returns {Promise} The serialized transaction. - * @throws {Error} If the UTXO transaction is invalid. - */ - public async signTransaction(tx: QiTransactionRequest): Promise { - const txobj = QiTransaction.from(tx); - if (!txobj.txInputs || txobj.txInputs.length == 0 || !txobj.txOutputs) - throw new Error('Invalid UTXO transaction, missing inputs or outputs'); + } + + public getOutpoints(zone: Zone): OutpointInfo[] { + this.validateZone(zone); + return this._outpoints.filter((outpoint) => outpoint.zone === zone); + } + + /** + * Signs a Qi transaction and returns the serialized transaction + * + * @param {QiTransactionRequest} tx - The transaction to sign. + * + * @returns {Promise} The serialized transaction. + * @throws {Error} If the UTXO transaction is invalid. + */ + public async signTransaction(tx: QiTransactionRequest): Promise { + const txobj = QiTransaction.from(tx); + if (!txobj.txInputs || txobj.txInputs.length == 0 || !txobj.txOutputs) + throw new Error('Invalid UTXO transaction, missing inputs or outputs'); const hash = keccak_256(txobj.unsignedSerialized); @@ -108,19 +107,19 @@ export class QiHDWallet extends AbstractHDWallet { return txobj.serialized; } - public async sendTransaction(tx: QiTransactionRequest): Promise { - if (!this.provider) { - throw new Error("Provider is not set"); - } - if (!tx.inputs || tx.inputs.length === 0) { - throw new Error('Transaction has no inputs'); - } - const input = tx.inputs[0]; - const address = computeAddress(hexlify(input.pub_key)); - const shard = getZoneForAddress(address); - if (!shard) { - throw new Error(`Address ${address} not found in any shard`); - } + public async sendTransaction(tx: QiTransactionRequest): Promise { + if (!this.provider) { + throw new Error('Provider is not set'); + } + if (!tx.inputs || tx.inputs.length === 0) { + throw new Error('Transaction has no inputs'); + } + const input = tx.inputs[0]; + const address = computeAddress(hexlify(input.pub_key)); + const shard = getZoneForAddress(address); + if (!shard) { + throw new Error(`Address ${address} not found in any shard`); + } // verify all inputs are from the same shard if (tx.inputs.some((input) => getZoneForAddress(computeAddress(hexlify(input.pub_key))) !== shard)) { @@ -197,95 +196,94 @@ export class QiHDWallet extends AbstractHDWallet { return addressNode.privateKey; } - // scan scans the specified zone for addresses with unspent outputs. - // Starting at index 0, tt will generate new addresses until - // the gap limit is reached for both gap and change addresses. - public async scan(zone: Zone, account: number = 0): Promise { - this.validateZone(zone); - // flush the existing addresses and outpoints - this._addresses = new Map(); - this._changeAddresses = new Map(); - this._gapAddresses = []; - this._gapChangeAddresses = []; - this._outpoints = []; + // scan scans the specified zone for addresses with unspent outputs. + // Starting at index 0, tt will generate new addresses until + // the gap limit is reached for both gap and change addresses. + public async scan(zone: Zone, account: number = 0): Promise { + this.validateZone(zone); + // flush the existing addresses and outpoints + this._addresses = new Map(); + this._changeAddresses = new Map(); + this._gapAddresses = []; + this._gapChangeAddresses = []; + this._outpoints = []; await this._scan(zone, account); } - // sync scans the specified zone for addresses with unspent outputs. - // Starting at the last address index, it will generate new addresses until - // the gap limit is reached for both gap and change addresses. - // If no account is specified, it will scan all accounts known to the wallet - public async sync(zone: Zone, account?: number): Promise { - this.validateZone(zone); - if (account) { - await this._scan(zone, account); - } else { - for (const account of this._accounts.keys()) { - await this._scan(zone, account); - } - } - } - - private async _scan(zone: Zone, account: number = 0): Promise { - if (!this.provider) throw new Error('Provider not set'); - - if (!this._accounts.has(account)) { - this.addAccount(account); - } - - let gapAddressesCount = 0; - let changeGapAddressesCount = 0; - - // helper function to handle the common logic for both gap and change addresses - const handleAddressScanning = async ( - getAddressInfo: () => NeuteredAddressInfo, - addressesCount: number, - gapAddressesArray: NeuteredAddressInfo[], - ): Promise => { - const addressInfo = getAddressInfo(); - const outpoints = await this.getOutpointsByAddress(addressInfo.address); - if (outpoints.length === 0) { - addressesCount++; - gapAddressesArray.push(addressInfo) - } else { - addressesCount = 0; - gapAddressesArray = []; - const newOutpointsInfo = outpoints.map((outpoint) => ({ - outpoint, - address: addressInfo.address, - zone: zone, - })); - this._outpoints.push(...newOutpointsInfo); - } - return addressesCount; - }; - - // main loop to scan addresses up to the gap limit - while (gapAddressesCount < QiHDWallet._GAP_LIMIT || changeGapAddressesCount < QiHDWallet._GAP_LIMIT) { - [gapAddressesCount, changeGapAddressesCount] = await Promise.all([ - gapAddressesCount < QiHDWallet._GAP_LIMIT - ? handleAddressScanning( - () => this.getNextAddress(account, zone), - gapAddressesCount, - this._gapAddresses, - ) - : gapAddressesCount, - - changeGapAddressesCount < QiHDWallet._GAP_LIMIT - ? handleAddressScanning( - () => this.getNextChangeAddress(account, zone), - changeGapAddressesCount, - this._gapChangeAddresses, - ) - : changeGapAddressesCount, - ]); - } - } - - - // getOutpointsByAddress queries the network node for the outpoints of the specified address - private async getOutpointsByAddress(address: string): Promise { + // sync scans the specified zone for addresses with unspent outputs. + // Starting at the last address index, it will generate new addresses until + // the gap limit is reached for both gap and change addresses. + // If no account is specified, it will scan all accounts known to the wallet + public async sync(zone: Zone, account?: number): Promise { + this.validateZone(zone); + if (account) { + await this._scan(zone, account); + } else { + for (const account of this._accounts.keys()) { + await this._scan(zone, account); + } + } + } + + private async _scan(zone: Zone, account: number = 0): Promise { + if (!this.provider) throw new Error('Provider not set'); + + if (!this._accounts.has(account)) { + this.addAccount(account); + } + + let gapAddressesCount = 0; + let changeGapAddressesCount = 0; + + // helper function to handle the common logic for both gap and change addresses + const handleAddressScanning = async ( + getAddressInfo: () => NeuteredAddressInfo, + addressesCount: number, + gapAddressesArray: NeuteredAddressInfo[], + ): Promise => { + const addressInfo = getAddressInfo(); + const outpoints = await this.getOutpointsByAddress(addressInfo.address); + if (outpoints.length === 0) { + addressesCount++; + gapAddressesArray.push(addressInfo); + } else { + addressesCount = 0; + gapAddressesArray = []; + const newOutpointsInfo = outpoints.map((outpoint) => ({ + outpoint, + address: addressInfo.address, + zone: zone, + })); + this._outpoints.push(...newOutpointsInfo); + } + return addressesCount; + }; + + // main loop to scan addresses up to the gap limit + while (gapAddressesCount < QiHDWallet._GAP_LIMIT || changeGapAddressesCount < QiHDWallet._GAP_LIMIT) { + [gapAddressesCount, changeGapAddressesCount] = await Promise.all([ + gapAddressesCount < QiHDWallet._GAP_LIMIT + ? handleAddressScanning( + () => this.getNextAddress(account, zone), + gapAddressesCount, + this._gapAddresses, + ) + : gapAddressesCount, + + changeGapAddressesCount < QiHDWallet._GAP_LIMIT + ? handleAddressScanning( + () => this.getNextChangeAddress(account, zone), + changeGapAddressesCount, + this._gapChangeAddresses, + ) + : changeGapAddressesCount, + ]); + } + } + + // getOutpointsByAddress queries the network node for the outpoints of the specified address + private async getOutpointsByAddress(address: string): Promise { try { const outpointsMap = await this.provider!.getOutpointsByAddress(address); if (!outpointsMap) { @@ -297,21 +295,21 @@ export class QiHDWallet extends AbstractHDWallet { } } - public getChangeAddressesForZone(zone: Zone): NeuteredAddressInfo[] { - this.validateZone(zone); - const changeAddresses = this._changeAddresses.values(); - return Array.from(changeAddresses).filter((addressInfo) => addressInfo.zone === zone); - } - - public getGapAddressesForZone(zone: Zone): NeuteredAddressInfo[] { - this.validateZone(zone); - const gapAddresses = this._gapAddresses.filter((addressInfo) => addressInfo.zone === zone); - return gapAddresses; - } - - public getGapChangeAddressesForZone(zone: Zone): NeuteredAddressInfo[] { - this.validateZone(zone); - const gapChangeAddresses = this._gapChangeAddresses.filter((addressInfo) => addressInfo.zone === zone); - return gapChangeAddresses; - } + public getChangeAddressesForZone(zone: Zone): NeuteredAddressInfo[] { + this.validateZone(zone); + const changeAddresses = this._changeAddresses.values(); + return Array.from(changeAddresses).filter((addressInfo) => addressInfo.zone === zone); + } + + public getGapAddressesForZone(zone: Zone): NeuteredAddressInfo[] { + this.validateZone(zone); + const gapAddresses = this._gapAddresses.filter((addressInfo) => addressInfo.zone === zone); + return gapAddresses; + } + + public getGapChangeAddressesForZone(zone: Zone): NeuteredAddressInfo[] { + this.validateZone(zone); + const gapChangeAddresses = this._gapChangeAddresses.filter((addressInfo) => addressInfo.zone === zone); + return gapChangeAddresses; + } } diff --git a/src/wallet/quai-hdwallet.ts b/src/wallet/quai-hdwallet.ts index 72a90c45..41002c62 100644 --- a/src/wallet/quai-hdwallet.ts +++ b/src/wallet/quai-hdwallet.ts @@ -1,9 +1,8 @@ - -import { AbstractHDWallet } from './hdwallet'; -import { HDNodeWallet } from "./hdnodewallet"; +import { AbstractHDWallet } from './hdwallet.js'; +import { HDNodeWallet } from './hdnodewallet.js'; import { QuaiTransactionRequest, Provider, TransactionResponse } from '../providers/index.js'; import { resolveAddress } from '../address/index.js'; -import { AllowedCoinType } from "../constants/index.js"; +import { AllowedCoinType } from '../constants/index.js'; export class QuaiHDWallet extends AbstractHDWallet { protected static _coinType: AllowedCoinType = 994; @@ -34,7 +33,7 @@ export class QuaiHDWallet extends AbstractHDWallet { return signedTx; } - public async sendTransaction(tx: QuaiTransactionRequest): Promise { + public async sendTransaction(tx: QuaiTransactionRequest): Promise { if (!this.provider) { throw new Error('Provider is not set'); } From c1a8303eac080202ff47c283ec68269de1514018 Mon Sep 17 00:00:00 2001 From: robertlincecum Date: Fri, 14 Jun 2024 11:06:02 -0500 Subject: [PATCH 2/2] force from and to on hash --- src/contract/contract.ts | 5 ++++- src/transaction/quai-transaction.ts | 11 +++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/contract/contract.ts b/src/contract/contract.ts index d049a02a..eea6097f 100644 --- a/src/contract/contract.ts +++ b/src/contract/contract.ts @@ -366,7 +366,7 @@ function buildWrappedMethod< }); const pop = await populateTransaction(...args); if (!pop.from && 'address' in runner && typeof runner.address === 'string') { - pop.from = runner.address; + pop.from = await resolveAddress(runner.address); } const tx = (await runner.sendTransaction(await pop)) as QuaiTransactionResponse; @@ -391,6 +391,9 @@ function buildWrappedMethod< operation: 'call', }); const tx = await populateTransaction(...args); + if (!tx.from && 'address' in runner && typeof runner.address === 'string') { + tx.from = await resolveAddress(runner.address); + } let result = '0x'; try { diff --git a/src/transaction/quai-transaction.ts b/src/transaction/quai-transaction.ts index 1367eb9d..777abab7 100644 --- a/src/transaction/quai-transaction.ts +++ b/src/transaction/quai-transaction.ts @@ -130,8 +130,11 @@ export class QuaiTransaction extends AbstractTransaction implements Q if (!this.originZone) { throw new Error('Invalid Zone for from address'); } + if (!(this.from && this.to)) { + throw new Error('Missing from or to address'); + } - const isSameLedger = !(this.from && this.to) || isQiAddress(this.from) === isQiAddress(this.to); + const isSameLedger = isQiAddress(this.from) === isQiAddress(this.to); if (this.isExternal && !isSameLedger) { throw new Error('Cross-zone & cross-ledger transactions are not supported'); } @@ -142,11 +145,7 @@ export class QuaiTransaction extends AbstractTransaction implements Q const hashHex = keccak256(dataBuffer); const hashBuffer = Buffer.from(hashHex.substring(2), 'hex'); - const origin = this.originZone - ? parseInt(this.originZone.slice(2), 16) - : this.destZone - ? parseInt(this.destZone.slice(2), 16) - : 0; + const origin = this.originZone ? parseInt(this.originZone.slice(2), 16) : 0; hashBuffer[0] = origin; hashBuffer[1] &= 0x7f; hashBuffer[2] = origin;