diff --git a/src/address/address.ts b/src/address/address.ts index f6dabfe2..1850fa2e 100644 --- a/src/address/address.ts +++ b/src/address/address.ts @@ -14,7 +14,7 @@ import { import type { SignatureLike } from '../crypto/index.js'; -function getChecksumAddress(address: string): string { +export function formatMixedCaseChecksumAddress(address: string): string { address = address.toLowerCase(); const chars = address.substring(2).split(''); @@ -39,11 +39,11 @@ function getChecksumAddress(address: string): string { } /** - * Returns a normalized and checksumed address for `address`. This accepts non-checksum addresses, checksum addresses - * and [[getIcapAddress]] formats. + * Returns a normalized and checksumed address for `address`. This accepts non-checksum addressesa and checksum + * addresses. * - * The checksum in Ethereum uses the capitalization (upper-case vs lower-case) of the characters within an address to - * encode its checksum, which offers, on average, a checksum of 15-bits. + * The checksum in Quai uses the capitalization (upper-case vs lower-case) of the characters within an address to encode + * its checksum, which offers, on average, a checksum of 15-bits. * * If `address` contains both upper-case and lower-case, it is assumed to already be a checksum address and its checksum * is validated, and if the address fails its expected checksum an error is thrown. @@ -60,19 +60,11 @@ function getChecksumAddress(address: string): string { * getAddress('0x8ba1f109551bd432803012645ac136ddd64dba72'); * //_result: * - * // Converts ICAP address and adds checksum - * getAddress('XE65GB6LDNXYOFTX0NSV3FUWKOWIXAMJK36'); - * //_result: - * * // Throws an error if an address contains mixed case, * // but the checksum fails * getAddress('0x8Ba1f109551bD432803012645Ac136ddd64DBA72'); * //_error: * ``` - * - * @todo Revise this documentation as ICAP addresses are not supported - * - * @todo GetIcapAddress has been removed, link must be revised or removed */ export function getAddress(address: string): string { assertArgument(typeof address === 'string', 'invalid address', 'address', address); @@ -83,12 +75,13 @@ export function getAddress(address: string): string { address = '0x' + address; } - const result = getChecksumAddress(address); + const result = formatMixedCaseChecksumAddress(address); - // It is a checksummed address with a bad checksum + // If original address is mix cased and recomputed version doesn't + // match the original this could indicate a potential typo or mispaste. assertArgument( !address.match(/([A-F].*[a-f])|([a-f].*[A-F])/) || result === address, - 'bad address checksum', + 'invalid address checksum', 'address', address, ); @@ -96,7 +89,7 @@ export function getAddress(address: string): string { return result; } - assertArgument(false, 'invalid address', 'address', address); + assertArgument(false, 'invalid address string format', 'address', address); } export function getContractAddress(from: string, nonce: BigNumberish, data: BytesLike): string { @@ -135,4 +128,4 @@ export function computeAddress(key: string | SigningKey): string { */ export function recoverAddress(digest: BytesLike, signature: SignatureLike): string { return computeAddress(SigningKey.recoverPublicKey(digest, signature)); -} \ No newline at end of file +} diff --git a/src/address/checks.ts b/src/address/checks.ts index 0856d4e2..b3245302 100644 --- a/src/address/checks.ts +++ b/src/address/checks.ts @@ -1,6 +1,6 @@ import { assertArgument } from '../utils/index.js'; -import { getAddress } from './address.js'; +import { formatMixedCaseChecksumAddress, getAddress } from './address.js'; import type { Addressable, AddressLike } from './index.js'; @@ -63,7 +63,7 @@ async function checkAddress(target: any, promise: Promise): Promi if (result == null || result === '0x0000000000000000000000000000000000000000') { assertArgument(false, 'invalid AddressLike value; did not resolve to a value address', 'target', target); } - return getAddress(result); + return result; } /** @@ -88,8 +88,6 @@ async function checkAddress(target: any, promise: Promise): Promi * contract = new Contract(addr, []); * resolveAddress(contract, provider); * //_result: - * - * * ``` * * @param {AddressLike} target - The target to resolve to an address. @@ -100,7 +98,7 @@ async function checkAddress(target: any, promise: Promise): Promi export function resolveAddress(target: AddressLike): string | Promise { if (typeof target === 'string') { if (target.match(/^0x[0-9a-f]{40}$/i)) { - return getAddress(target); + return target; } } else if (isAddressable(target)) { return checkAddress(target, target.getAddress()); @@ -110,3 +108,22 @@ export function resolveAddress(target: AddressLike): string | Promise { assertArgument(false, 'unsupported addressable value', 'target', target); } + +/** + * Checks if the address is a valid mixed case checksummed address. + * + * @category Address + * @param address - The address to validate. + * + * @returns True if the address is a valid mixed case checksummed address. + */ +export function validateAddress(address: string): void { + assertArgument(typeof address === 'string', 'address must be string', 'address', address); + assertArgument( + Boolean(address.match(/^(0x)?[0-9a-fA-F]{40}$/)), + 'invalid address string format', + 'address', + address, + ); + assertArgument(formatMixedCaseChecksumAddress(address) === address, 'invalid address checksum', 'address', address); +} diff --git a/src/address/index.ts b/src/address/index.ts index 6c8cd27a..a5000c1a 100644 --- a/src/address/index.ts +++ b/src/address/index.ts @@ -35,4 +35,4 @@ export { getAddress, computeAddress, recoverAddress } from './address.js'; export { getCreateAddress, getCreate2Address } from './contract-address.js'; -export { isAddressable, isAddress, resolveAddress } from './checks.js'; +export { isAddressable, isAddress, resolveAddress, validateAddress } from './checks.js'; diff --git a/src/contract/contract.ts b/src/contract/contract.ts index fea12455..a07d72ea 100644 --- a/src/contract/contract.ts +++ b/src/contract/contract.ts @@ -1,5 +1,5 @@ import { Interface, Typed } from '../abi/index.js'; -import { isAddressable, resolveAddress } from '../address/index.js'; +import { isAddressable, resolveAddress, validateAddress } from '../address/index.js'; // import from provider.ts instead of index.ts to prevent circular dep // from quaiscanProvider import { @@ -204,9 +204,11 @@ function buildWrappedFallback(contract: BaseContract): WrappedFallback { const tx: ContractTransaction = await copyOverrides<'data'>(overrides, ['data']); tx.to = await contract.getAddress(); + validateAddress(tx.to); if (tx.from) { tx.from = await resolveAddress(tx.from); + validateAddress(tx.from); } const iface = contract.interface; @@ -748,8 +750,8 @@ export class BaseContract implements Addressable, EventEmitterable = Array, I = BaseContract ); if (this.runner instanceof Wallet) { + validateAddress(this.runner.address); tx.from = this.runner.address; } const grindedTx = await this.grindContractAddress(tx); @@ -168,10 +169,9 @@ export class ContractFactory = Array, I = BaseContract let i = 0; const startingData = tx.data; while (i < 10000) { - var contractAddress = getContractAddress(sender, BigInt(tx.nonce || 0), tx.data || ''); - var contractShard = getShardForAddress(contractAddress); - console.log("contractAddress ", contractAddress); - var utxo = isQiAddress(contractAddress); + const contractAddress = getContractAddress(sender, BigInt(tx.nonce || 0), tx.data || ''); + const contractShard = getShardForAddress(contractAddress); + const utxo = isQiAddress(contractAddress); if (contractShard === toShard && !utxo) { return tx; } diff --git a/src/hash/typed-data.ts b/src/hash/typed-data.ts index d01b239e..ef154d66 100644 --- a/src/hash/typed-data.ts +++ b/src/hash/typed-data.ts @@ -127,7 +127,7 @@ const domainChecks: Record any> = { }, verifyingContract: function (value: any) { try { - return getAddress(value).toLowerCase(); + return getAddress(value); // eslint-disable-next-line no-empty } catch (error) {} assertArgument(false, `invalid domain value "verifyingContract"`, 'domain.verifyingContract', value); diff --git a/src/providers/abstract-provider.ts b/src/providers/abstract-provider.ts index 831d92b9..b6e98cf6 100644 --- a/src/providers/abstract-provider.ts +++ b/src/providers/abstract-provider.ts @@ -8,7 +8,7 @@ // @TODO // Event coalescence -// When we register an event with an async value (e.g. address is a Signer), +// When we register an event with an async value (e.g. address is a Signer), // we need to add it immeidately for the Event API, but also // need time to resolve the address. Upon resolving the address, we need to // migrate the listener to the static event. We also need to maintain a map @@ -74,11 +74,11 @@ import type { Provider, ProviderEvent, TransactionRequest, -} from "./provider.js"; -import { WorkObjectLike } from "../transaction/work-object.js"; -import {QiTransaction, QuaiTransaction} from "../transaction/index.js"; -import {QuaiTransactionResponseParams} from "./formatting.js"; -import {keccak256, SigningKey} from "../crypto/index.js"; +} from './provider.js'; +import { WorkObjectLike } from '../transaction/work-object.js'; +import { QiTransaction, QuaiTransaction } from '../transaction/index.js'; +import { QuaiTransactionResponseParams } from './formatting.js'; +import { keccak256, SigningKey } from '../crypto/index.js'; type Timer = ReturnType; @@ -925,8 +925,8 @@ export class AbstractProvider implements Provider { } /** - * Returns or resolves to the address for `address`, resolving {@link Addressable | **Addressable**} - * objects and returning if already an address. + * Returns or resolves to the address for `address`, resolving {@link Addressable | **Addressable**} objects and + * returning if already an address. * * @param {AddressLike} address - The address to normalize. * @@ -985,8 +985,8 @@ export class AbstractProvider implements Provider { } /** - * Returns or resolves to a filter for `filter`, resolving any {@link Addressable | **Addressable**} - * object and returning if already a valid filter. + * Returns or resolves to a filter for `filter`, resolving any {@link Addressable | **Addressable**} object and + * returning if already a valid filter. * * @param {Filter | FilterByBlockHash} filter - The filter to normalize. * @@ -1086,8 +1086,8 @@ export class AbstractProvider implements Provider { } /** - * Returns or resovles to a transaction for `request`, resolving any - * {@link Addressable | **Addressable**} and returning if already a valid transaction. + * Returns or resovles to a transaction for `request`, resolving any {@link Addressable | **Addressable**} and + * returning if already a valid transaction. * * @param {PerformActionTransaction} _request - The transaction to normalize. * @@ -1107,10 +1107,8 @@ export class AbstractProvider implements Provider { ? 'address' in request[key][0] ? ((request)[key]).map((it) => resolveAddress(hexlify(it.address))) : ((request)[key]).map((it) => - resolveAddress( - getAddress( - keccak256('0x' + SigningKey.computePublicKey(it.pub_key).substring(4)).substring(26), - ), + getAddress( + keccak256('0x' + SigningKey.computePublicKey(it.pub_key).substring(4)).substring(26), ), ) : resolveAddress((request)[key]); @@ -1279,7 +1277,7 @@ export class AbstractProvider implements Provider { async #call(tx: PerformActionTransaction, blockTag: string, attempt: number, shard?: string): Promise { // This came in as a PerformActionTransaction, so to/from are safe; we can cast const transaction = copyRequest(tx); - return hexlify(await this._perform({ method: "call", transaction, blockTag, shard })); + return hexlify(await this._perform({ method: 'call', transaction, blockTag, shard })); } // TODO: `shard` is not used, remove or re-write diff --git a/src/providers/provider.ts b/src/providers/provider.ts index fcd2e774..d2faeb37 100644 --- a/src/providers/provider.ts +++ b/src/providers/provider.ts @@ -126,7 +126,6 @@ export class FeeData { } export function addressFromTransactionRequest(tx: TransactionRequest): AddressLike { - //return 'from' in tx ? tx.from : tx.inputs[0].address; if ('from' in tx) { return tx.from; } diff --git a/src/quais.ts b/src/quais.ts index 89de19fe..5b5e58ac 100644 --- a/src/quais.ts +++ b/src/quais.ts @@ -1,39 +1,68 @@ // VERSION -export { version } from "./_version.js"; +export { version } from './_version.js'; // APPLICATION BINARY INTERFACE export { - decodeBytes32String, encodeBytes32String, - + decodeBytes32String, + encodeBytes32String, AbiCoder, - ConstructorFragment, ErrorFragment, EventFragment, Fragment, FallbackFragment, FunctionFragment, NamedFragment, ParamType, StructFragment, - - checkResultErrors, ErrorDescription, Indexed, Interface, LogDescription, Result, TransactionDescription, + ConstructorFragment, + ErrorFragment, + EventFragment, + Fragment, + FallbackFragment, + FunctionFragment, + NamedFragment, + ParamType, + StructFragment, + checkResultErrors, + ErrorDescription, + Indexed, + Interface, + LogDescription, + Result, + TransactionDescription, Typed, -} from "./abi/index.js"; +} from './abi/index.js'; // ADDRESS export { getAddress, - computeAddress, recoverAddress, - getCreateAddress, getCreate2Address, - isAddressable, isAddress, resolveAddress -} from "./address/index.js"; + computeAddress, + recoverAddress, + getCreateAddress, + getCreate2Address, + isAddressable, + isAddress, + resolveAddress, + validateAddress, +} from './address/index.js'; //CONSTANTS export { ZeroAddress, - WeiPerEther, MaxUint256, MinInt256, MaxInt256, N, + WeiPerEther, + MaxUint256, + MinInt256, + MaxInt256, + N, ZeroHash, - quaisymbol, MessagePrefix -} from "./constants/index.js"; + quaisymbol, + MessagePrefix, +} from './constants/index.js'; // CONTRACT export { - BaseContract, Contract, + BaseContract, + Contract, ContractFactory, - ContractEventPayload, ContractTransactionReceipt, ContractTransactionResponse, ContractUnknownEventPayload, EventLog, UndecodedEventLog -} from "./contract/index.js"; + ContractEventPayload, + ContractTransactionReceipt, + ContractTransactionResponse, + ContractUnknownEventPayload, + EventLog, + UndecodedEventLog, +} from './contract/index.js'; // CRYPTO export { @@ -41,182 +70,266 @@ export { randomBytes, keccak256, ripemd160, - sha256, sha512, + sha256, + sha512, pbkdf2, - scrypt, scryptSync, + scrypt, + scryptSync, lock, - Signature, SigningKey -} from "./crypto/index.js"; + Signature, + SigningKey, +} from './crypto/index.js'; // HASH export { id, - hashMessage, verifyMessage, - solidityPacked, solidityPackedKeccak256, solidityPackedSha256, + hashMessage, + verifyMessage, + solidityPacked, + solidityPackedKeccak256, + solidityPackedSha256, TypedDataEncoder, - verifyTypedData -} from "./hash/index.js"; + verifyTypedData, +} from './hash/index.js'; // PROVIDERS export { - Block, FeeData, Log, TransactionReceipt, TransactionResponse, - + Block, + FeeData, + Log, + TransactionReceipt, + TransactionResponse, AbstractProvider, - - JsonRpcApiProvider, JsonRpcProvider, - + JsonRpcApiProvider, + JsonRpcProvider, BrowserProvider, - - SocketProvider, WebSocketProvider, - + SocketProvider, + WebSocketProvider, Network, + SocketBlockSubscriber, + SocketEventSubscriber, + SocketPendingSubscriber, + SocketSubscriber, + UnmanagedSubscriber, + copyRequest, +} from './providers/index.js'; - SocketBlockSubscriber, SocketEventSubscriber, SocketPendingSubscriber, - SocketSubscriber, UnmanagedSubscriber, - - copyRequest -} from "./providers/index.js"; - -export { - AbstractSigner, VoidSigner, -} from "./signers/index.js"; +export { AbstractSigner, VoidSigner } from './signers/index.js'; // TRANSACTION -export { - accessListify, - AbstractTransaction, FewestCoinSelector, - QiTransaction -} from "./transaction/index.js"; +export { accessListify, AbstractTransaction, FewestCoinSelector, QiTransaction } from './transaction/index.js'; // UTILS export { - concat, dataLength, dataSlice, getBytes, getBytesCopy, hexlify, - isHexString, isBytesLike, stripZerosLeft, zeroPadBytes, zeroPadValue, - defineProperties, resolveProperties, - assert, assertArgument, assertArgumentCount, assertNormalize, assertPrivate, + concat, + dataLength, + dataSlice, + getBytes, + getBytesCopy, + hexlify, + isHexString, + isBytesLike, + stripZerosLeft, + zeroPadBytes, + zeroPadValue, + defineProperties, + resolveProperties, + assert, + assertArgument, + assertArgumentCount, + assertNormalize, + assertPrivate, makeError, - isCallException, isError, + isCallException, + isError, EventPayload, - FetchRequest, FetchResponse, FetchCancelSignal, + FetchRequest, + FetchResponse, + FetchCancelSignal, FixedNumber, - getBigInt, getNumber, getUint, toBeArray, toBigInt, toBeHex, toNumber, toQuantity, - fromTwos, toTwos, mask, - formatQuai, parseQuai, formatEther, parseEther, formatUnits, parseUnits, - uuidV4, getTxType, getShardForAddress, getAddressDetails, isQiAddress, -} from "./utils/index.js"; + getBigInt, + getNumber, + getUint, + toBeArray, + toBigInt, + toBeHex, + toNumber, + toQuantity, + fromTwos, + toTwos, + mask, + formatQuai, + parseQuai, + formatEther, + parseEther, + formatUnits, + parseUnits, + uuidV4, + getTxType, + getShardForAddress, + getAddressDetails, + isQiAddress, +} from './utils/index.js'; export { - decodeBase58, encodeBase58, - decodeBase64, encodeBase64, - decodeProtoTransaction, encodeProtoTransaction, - decodeProtoWorkObject, encodeProtoWorkObject, - toUtf8Bytes, toUtf8CodePoints, toUtf8String, Utf8ErrorFuncs, -} from "./encoding/index.js"; + decodeBase58, + encodeBase58, + decodeBase64, + encodeBase64, + decodeProtoTransaction, + encodeProtoTransaction, + decodeProtoWorkObject, + encodeProtoWorkObject, + toUtf8Bytes, + toUtf8CodePoints, + toUtf8String, + Utf8ErrorFuncs, +} from './encoding/index.js'; // WALLET export { Mnemonic, - BaseWallet, QuaiHDWallet, HDNodeVoidWallet, QiHDWallet, + BaseWallet, + QuaiHDWallet, + HDNodeVoidWallet, + QiHDWallet, Wallet, isKeystoreJson, - decryptKeystoreJsonSync, decryptKeystoreJson, - encryptKeystoreJson, encryptKeystoreJsonSync, -} from "./wallet/index.js"; + decryptKeystoreJsonSync, + decryptKeystoreJson, + encryptKeystoreJson, + encryptKeystoreJsonSync, +} from './wallet/index.js'; // WORDLIST -export { - Wordlist, LangEn, LangEs, WordlistOwl, WordlistOwlA, wordlists -} from "./wordlists/index.js"; - - +export { Wordlist, LangEn, LangEs, WordlistOwl, WordlistOwlA, wordlists } from './wordlists/index.js'; ///////////////////////////// // Types // APPLICATION BINARY INTERFACE export type { - JsonFragment, JsonFragmentType, - FormatType, FragmentType, + JsonFragment, + JsonFragmentType, + FormatType, + FragmentType, InterfaceAbi, - ParamTypeWalkFunc, ParamTypeWalkAsyncFunc -} from "./abi/index.js"; + ParamTypeWalkFunc, + ParamTypeWalkAsyncFunc, +} from './abi/index.js'; // ADDRESS -export type { - Addressable, AddressLike -} from "./address/index.js"; +export type { Addressable, AddressLike } from './address/index.js'; // CONTRACT export type { - ConstantContractMethod, ContractEvent, ContractEventArgs, ContractEventName, - ContractInterface, ContractMethod, ContractMethodArgs, ContractTransaction, - DeferredTopicFilter, Overrides, - ContractRunner, BaseContractMethod, ContractDeployTransaction, PostfixOverrides, - WrappedFallback -} from "./contract/index.js"; + ConstantContractMethod, + ContractEvent, + ContractEventArgs, + ContractEventName, + ContractInterface, + ContractMethod, + ContractMethodArgs, + ContractTransaction, + DeferredTopicFilter, + Overrides, + ContractRunner, + BaseContractMethod, + ContractDeployTransaction, + PostfixOverrides, + WrappedFallback, +} from './contract/index.js'; // CRYPTO -export type { ProgressCallback, SignatureLike } from "./crypto/index.js"; +export type { ProgressCallback, SignatureLike } from './crypto/index.js'; // HASH -export type { TypedDataDomain, TypedDataField } from "./hash/index.js"; +export type { TypedDataDomain, TypedDataField } from './hash/index.js'; // PROVIDERS export type { Provider, - AbstractProviderOptions, - - AbstractProviderPlugin, BlockParams, BlockTag, DebugEventBrowserProvider, - Eip1193Provider, EventFilter, Filter, FilterByBlockHash, - JsonRpcApiProviderOptions, JsonRpcError, JsonRpcPayload, JsonRpcResult, - JsonRpcTransactionRequest, LogParams, MinedBlock, MinedTransactionResponse, Networkish, - OrphanFilter, PerformActionFilter, PerformActionRequest, PerformActionTransaction, - PreparedTransactionRequest, ProviderEvent, Subscriber, Subscription, TopicFilter, - TransactionReceiptParams, TransactionRequest, TransactionResponseParams, - WebSocketCreator, WebSocketLike -} from "./providers/index.js"; + AbstractProviderPlugin, + BlockParams, + BlockTag, + DebugEventBrowserProvider, + Eip1193Provider, + EventFilter, + Filter, + FilterByBlockHash, + JsonRpcApiProviderOptions, + JsonRpcError, + JsonRpcPayload, + JsonRpcResult, + JsonRpcTransactionRequest, + LogParams, + MinedBlock, + MinedTransactionResponse, + Networkish, + OrphanFilter, + PerformActionFilter, + PerformActionRequest, + PerformActionTransaction, + PreparedTransactionRequest, + ProviderEvent, + Subscriber, + Subscription, + TopicFilter, + TransactionReceiptParams, + TransactionRequest, + TransactionResponseParams, + WebSocketCreator, + WebSocketLike, +} from './providers/index.js'; // SIGNERS -export type { - Signer, -} from "./signers/index.js"; +export type { Signer } from './signers/index.js'; // TRANSACTION -export type { - AccessList, AccessListish, AccessListEntry, - TransactionLike -} from "./transaction/index.js"; +export type { AccessList, AccessListish, AccessListEntry, TransactionLike } from './transaction/index.js'; // UTILS export type { BytesLike, - BigNumberish, Numeric, + BigNumberish, + Numeric, ErrorCode, FixedFormat, - GetUrlResponse, - FetchPreflightFunc, FetchProcessFunc, FetchRetryFunc, - FetchGatewayFunc, FetchGetUrlFunc, - - quaisError, UnknownError, NotImplementedError, UnsupportedOperationError, NetworkError, - ServerError, TimeoutError, BadDataError, CancelledError, BufferOverrunError, - NumericFaultError, InvalidArgumentError, MissingArgumentError, UnexpectedArgumentError, - CallExceptionError, InsufficientFundsError, NonceExpiredError, - ReplacementUnderpricedError, TransactionReplacedError, + FetchPreflightFunc, + FetchProcessFunc, + FetchRetryFunc, + FetchGatewayFunc, + FetchGetUrlFunc, + quaisError, + UnknownError, + NotImplementedError, + UnsupportedOperationError, + NetworkError, + ServerError, + TimeoutError, + BadDataError, + CancelledError, + BufferOverrunError, + NumericFaultError, + InvalidArgumentError, + MissingArgumentError, + UnexpectedArgumentError, + CallExceptionError, + InsufficientFundsError, + NonceExpiredError, + ReplacementUnderpricedError, + TransactionReplacedError, ActionRejectedError, CodedquaisError, + CallExceptionAction, + CallExceptionTransaction, + EventEmitterable, + Listener, +} from './utils/index.js'; - CallExceptionAction, CallExceptionTransaction, - EventEmitterable, Listener -} from "./utils/index.js"; - -export type { - Utf8ErrorFunc, UnicodeNormalizationForm, Utf8ErrorReason, -} from "./encoding/index.js"; +export type { Utf8ErrorFunc, UnicodeNormalizationForm, Utf8ErrorReason } from './encoding/index.js'; // WALLET -export type { - KeystoreAccount, EncryptOptions -} from "./wallet/index.js"; - +export type { KeystoreAccount, EncryptOptions } from './wallet/index.js'; diff --git a/src/signers/abstract-signer.ts b/src/signers/abstract-signer.ts index 0f0371b1..57a8f4de 100644 --- a/src/signers/abstract-signer.ts +++ b/src/signers/abstract-signer.ts @@ -4,22 +4,22 @@ * * @section api/providers/abstract-signer: Subclassing Signer [abstract-signer] */ -import { AddressLike, resolveAddress } from '../address/index.js'; +import { AddressLike, resolveAddress, validateAddress } from '../address/index.js'; +import { defineProperties, getBigInt, resolveProperties, assert, assertArgument, isQiAddress } from '../utils/index.js'; import { - defineProperties, getBigInt, resolveProperties, - assert, assertArgument, isQiAddress -} from "../utils/index.js"; -import {addressFromTransactionRequest, copyRequest, QiTransactionRequest, QuaiTransactionRequest} from "../providers/provider.js"; + addressFromTransactionRequest, + copyRequest, + QiTransactionRequest, + QuaiTransactionRequest, +} from '../providers/provider.js'; import type { TypedDataDomain, TypedDataField } from '../hash/index.js'; import type { TransactionLike } from '../transaction/index.js'; -import type { - BlockTag, Provider, TransactionRequest, TransactionResponse -} from "../providers/provider.js"; -import type { Signer } from "./signer.js"; -import { getTxType } from "../utils/index.js"; -import {QiTransaction, QiTransactionLike, QuaiTransaction, QuaiTransactionLike} from "../transaction/index.js"; +import type { BlockTag, Provider, TransactionRequest, TransactionResponse } from '../providers/provider.js'; +import type { Signer } from './signer.js'; +import { getTxType } from '../utils/index.js'; +import { QiTransaction, QiTransactionLike, QuaiTransaction, QuaiTransactionLike } from '../transaction/index.js'; function checkProvider(signer: AbstractSigner, operation: string): Provider { if (signer.provider) { @@ -33,6 +33,7 @@ async function populate(signer: AbstractSigner, tx: TransactionRequest): Promise if (pop.to != null) { pop.to = resolveAddress(pop.to); + validateAddress(pop.to); } if (pop.from != null) { @@ -44,6 +45,7 @@ async function populate(signer: AbstractSigner, tx: TransactionRequest): Promise } else { pop.from = signer.getAddress(); } + validateAddress(pop.from); return await resolveProperties(pop); } @@ -192,7 +194,7 @@ export abstract class AbstractSigner

; diff --git a/src/transaction/accesslist.ts b/src/transaction/accesslist.ts index 738eec65..ff207955 100644 --- a/src/transaction/accesslist.ts +++ b/src/transaction/accesslist.ts @@ -1,9 +1,11 @@ +import { validateAddress } from '../address/index.js'; import { getAddress } from '../address/index.js'; import { assertArgument, isHexString } from '../utils/index.js'; import type { AccessList, AccessListish } from './index.js'; function accessSetify(addr: string, storageKeys: Array): { address: string; storageKeys: Array } { + validateAddress(addr); return { address: getAddress(addr), storageKeys: storageKeys.map((storageKey, index) => { diff --git a/src/transaction/quai-transaction.ts b/src/transaction/quai-transaction.ts index 167ec6af..68b12b6a 100644 --- a/src/transaction/quai-transaction.ts +++ b/src/transaction/quai-transaction.ts @@ -1,5 +1,5 @@ -import { keccak256, Signature } from "../crypto/index.js"; -import { AccessList, accessListify, AccessListish, AbstractTransaction, TransactionLike } from "./index.js"; +import { keccak256, Signature } from '../crypto/index.js'; +import { AccessList, accessListify, AccessListish, AbstractTransaction, TransactionLike } from './index.js'; import { assert, assertArgument, @@ -9,13 +9,16 @@ import { getBytes, getNumber, getShardForAddress, - hexlify, isQiAddress, - toBeArray, toBigInt, zeroPadValue -} from "../utils/index.js"; + hexlify, + isQiAddress, + toBeArray, + toBigInt, + zeroPadValue, +} from '../utils/index.js'; import { decodeProtoTransaction, encodeProtoTransaction } from '../encoding/index.js'; -import { getAddress, recoverAddress } from "../address/index.js"; -import { formatNumber, handleNumber } from "../providers/format.js"; -import { ProtoTransaction} from "./abstract-transaction.js"; +import { recoverAddress, validateAddress } from '../address/index.js'; +import { formatNumber, handleNumber } from '../providers/format.js'; +import { ProtoTransaction } from './abstract-transaction.js'; /** * @category Transaction @@ -112,7 +115,8 @@ export class QuaiTransaction extends AbstractTransaction implements Q return this.#to; } set to(value: null | string) { - this.#to = value == null ? null : getAddress(value); + if (value !== null) validateAddress(value); + this.#to = value; } get hash(): null | string { @@ -121,12 +125,13 @@ export class QuaiTransaction extends AbstractTransaction implements Q } return this.unsignedHash; } + get unsignedHash(): string { - const destUtxo = isQiAddress(this.to || ""); + const destUtxo = isQiAddress(this.to || ''); const originUtxo = isQiAddress(this.from); if (!this.originShard) { - throw new Error("Invalid Shard for from or to address"); + throw new Error('Invalid Shard for from or to address'); } if (this.isExternal && destUtxo !== originUtxo) { throw new Error('Cross-shard & cross-ledger transactions are not supported'); @@ -154,7 +159,7 @@ export class QuaiTransaction extends AbstractTransaction implements Q } get destShard(): string | undefined { - return this.to !== null ? getShardForAddress(this.to || "")?.byte.slice(2) : undefined; + return this.to !== null ? getShardForAddress(this.to || '')?.byte.slice(2) : undefined; } /** @@ -362,8 +367,8 @@ export class QuaiTransaction extends AbstractTransaction implements Q gas_fee_cap: formatNumber(this.maxFeePerGas || 0, 'maxFeePerGas'), gas: Number(this.gasLimit || 0), to: this.to != null ? getBytes(this.to as string) : null, - value: formatNumber(this.value || 0, "value"), - data: getBytes(this.data || "0x"), + value: formatNumber(this.value || 0, 'value'), + data: getBytes(this.data || '0x'), access_list: { access_tuples: [] }, }; @@ -394,6 +399,7 @@ export class QuaiTransaction extends AbstractTransaction implements Q result.type = tx.type; } if (tx.to != null) { + validateAddress(tx.to); result.to = tx.to; } if (tx.nonce != null) { @@ -429,6 +435,7 @@ 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); result.from = tx.from; } @@ -446,17 +453,13 @@ export class QuaiTransaction extends AbstractTransaction implements Q static fromProto(protoTx: ProtoTransaction): QuaiTransaction { // TODO: Fix this because new tx instance requires a 'from' address let signature: null | Signature = null; - let address; + let address: string = ''; if (protoTx.v && protoTx.r && protoTx.s) { // check if protoTx.r is zero - if (protoTx.r.reduce((acc, val) => acc += val, 0) == 0) { - throw new Error("Proto decoding only supported for signed transactions") + if (protoTx.r.reduce((acc, val) => (acc += val), 0) == 0) { + throw new Error('Proto decoding only supported for signed transactions'); } - const signatureFields = [ - hexlify(protoTx.v!), - hexlify(protoTx.r!), - hexlify(protoTx.s!), - ]; + const signatureFields = [hexlify(protoTx.v!), hexlify(protoTx.r!), hexlify(protoTx.s!)]; signature = _parseSignature(signatureFields); const protoTxCopy = structuredClone(protoTx); @@ -469,22 +472,25 @@ export class QuaiTransaction extends AbstractTransaction implements Q delete protoTxCopy.etx_index; address = recoverAddress(keccak256(encodeProtoTransaction(protoTxCopy)), signature); - } else { - address = ''; } - const tx = new QuaiTransaction(address); if (signature) { tx.signature = signature; } + + if (protoTx.to !== null) { + const toAddr = hexlify(protoTx.to!); + validateAddress(toAddr); + tx.to = toAddr; + } + tx.type = protoTx.type; tx.chainId = toBigInt(protoTx.chain_id); tx.nonce = Number(protoTx.nonce); tx.maxPriorityFeePerGas = toBigInt(protoTx.gas_tip_cap!); tx.maxFeePerGas = toBigInt(protoTx.gas_fee_cap!); tx.gasLimit = toBigInt(protoTx.gas!); - tx.to = protoTx.to !== null ? hexlify(protoTx.to!) : null; tx.value = protoTx.value !== null ? toBigInt(protoTx.value!) : BigInt(0); tx.data = hexlify(protoTx.data!); tx.accessList = protoTx.access_list!.access_tuples.map((tuple) => ({ diff --git a/src/transaction/utxo.ts b/src/transaction/utxo.ts index a44db9b7..8a362632 100644 --- a/src/transaction/utxo.ts +++ b/src/transaction/utxo.ts @@ -1,6 +1,6 @@ -import { getAddress } from "../address/index.js"; -import { getBigInt } from "../utils/index.js"; -import type { BigNumberish } from "../utils/index.js"; +import { validateAddress } from '../address/index.js'; +import { getBigInt } from '../utils/index.js'; +import type { BigNumberish } from '../utils/index.js'; /** * @category Transaction @@ -230,7 +230,8 @@ export class UTXO implements UTXOLike { return this.#address || ''; } set address(value: string) { - this.#address = getAddress(value); + validateAddress(value); + this.#address = value; } get denomination(): null | bigint { @@ -287,10 +288,18 @@ export class UTXO implements UTXOLike { } const result = utxo instanceof UTXO ? utxo : new UTXO(); - if (utxo.txhash != null) { result.txhash = utxo.txhash; } - if (utxo.index != null) { result.index = utxo.index; } - if (utxo.address != null && utxo.address !== '') { result.address = utxo.address; } - if (utxo.denomination != null) { result.denomination = utxo.denomination; } + if (utxo.txhash != null) { + result.txhash = utxo.txhash; + } + if (utxo.index != null) { + result.index = utxo.index; + } + if (utxo.address != null && utxo.address !== '') { + result.address = utxo.address; + } + if (utxo.denomination != null) { + result.denomination = utxo.denomination; + } return result; } diff --git a/src/wallet/base-wallet.ts b/src/wallet/base-wallet.ts index bcf861ca..e6a43a50 100644 --- a/src/wallet/base-wallet.ts +++ b/src/wallet/base-wallet.ts @@ -1,4 +1,4 @@ -import { getAddress, computeAddress, resolveAddress } from '../address/index.js'; +import { getAddress, computeAddress, resolveAddress, validateAddress } from '../address/index.js'; import { hashMessage, TypedDataEncoder } from '../hash/index.js'; import { AbstractSigner } from '../signers/index.js'; import { resolveProperties, assertArgument } from '../utils/index.js'; @@ -90,21 +90,27 @@ export class BaseWallet extends AbstractSigner { }); if (to != null) { + validateAddress(to); tx.to = to; } if (from != null) { + validateAddress(from); tx.from = from; } if (tx.from != null) { - assertArgument(getAddress((tx.from)) === this.#address, - "transaction from address mismatch", "tx.from", tx.from); + assertArgument( + getAddress(tx.from) === this.#address, + 'transaction from address mismatch', + 'tx.from', + tx.from, + ); } const btx = QuaiTransaction.from(tx); - console.log('unsigned', btx.unsignedSerialized) - const digest= keccak256(btx.unsignedSerialized) - btx.signature = this.signingKey.sign(digest) + console.log('unsigned', btx.unsignedSerialized); + const digest = keccak256(btx.unsignedSerialized); + btx.signature = this.signingKey.sign(digest); return btx.serialized; }