diff --git a/.changeset/chilly-buses-add.md b/.changeset/chilly-buses-add.md new file mode 100644 index 00000000000..b5210af54f6 --- /dev/null +++ b/.changeset/chilly-buses-add.md @@ -0,0 +1,8 @@ +--- +"@internal/check-imports": patch +"@fuel-ts/account": patch +"@fuel-ts/address": patch +"@fuel-ts/program": patch +--- + +chore: `Address` constructor now accepts a range of inputs. diff --git a/apps/docs/src/guide/provider/snippets/functionality/get-base-asset-id.ts b/apps/docs/src/guide/provider/snippets/functionality/get-base-asset-id.ts index 479feb1c13e..2c317b9338d 100644 --- a/apps/docs/src/guide/provider/snippets/functionality/get-base-asset-id.ts +++ b/apps/docs/src/guide/provider/snippets/functionality/get-base-asset-id.ts @@ -9,7 +9,7 @@ const baseAssetId = await provider.getBaseAssetId(); // 0x... // Instantiate our recipients address -const recipientAddress = Address.fromAddressOrString(WALLET_ADDRESS); +const recipientAddress = new Address(WALLET_ADDRESS); // Create a transaction request const transactionRequest = new ScriptTransactionRequest(); diff --git a/apps/docs/src/guide/types/address.md b/apps/docs/src/guide/types/address.md index 62b09430696..364216f1d61 100644 --- a/apps/docs/src/guide/types/address.md +++ b/apps/docs/src/guide/types/address.md @@ -24,18 +24,28 @@ To create an [`Address`](https://fuels-ts-docs-api.vercel.app/classes/_fuel_ts_a <<< @./snippets/address/from-a-public-key.ts#full{ts:line-numbers} -## Utility Functions +### From an EVM Address -The [`Address`](https://fuels-ts-docs-api.vercel.app/classes/_fuel_ts_address.Address.html) class also provides some practical utility functions: +To create an [`Address`](https://fuels-ts-docs-api.vercel.app/classes/_fuel_ts_address.Address.html) from an EVM address, use the following code snippet: -1. `fromString`: Create a new [`Address`](https://fuels-ts-docs-api.vercel.app/classes/_fuel_ts_address.Address.html) from an ambiguous source that may be a `B256` address: +<<< @./snippets/address/from-an-evm-address.ts#full{ts:line-numbers} -<<< @./snippets/address/utilities-function-1.ts#full{ts:line-numbers} +### From an existing Address -2. `fromDynamicInput`: Create a new [`Address`](https://fuels-ts-docs-api.vercel.app/classes/_fuel_ts_address.Address.html) when the address source is unknown: +To create an [`Address`](https://fuels-ts-docs-api.vercel.app/classes/_fuel_ts_address.Address.html) from an existing [`Address`](https://fuels-ts-docs-api.vercel.app/classes/_fuel_ts_address.Address.html) instance, use the following code snippet: -<<< @./snippets/address/utilities-function-2.ts#full{ts:line-numbers} +<<< @./snippets/address/from-an-existing-address.ts#full{ts:line-numbers} -3. `equals:` As you may already notice, the `equals` function can compare addresses instances: +## Utility functions -<<< @./snippets/address/utilities-function-3.ts#full{ts:line-numbers} +### `equals` + +As you may already notice, the `equals` function can compare addresses instances: + +<<< @./snippets/address/utilities-function-equals.ts#full{ts:line-numbers} + +### `toChecksum` + +To convert an address to a checksum address, use the `toChecksum` function: + +<<< @./snippets/address/utilities-function-to-checksum.ts#full{ts:line-numbers} diff --git a/apps/docs/src/guide/types/snippets/address/from-a-b256.ts b/apps/docs/src/guide/types/snippets/address/from-a-b256.ts index 01c1050b358..a96355e5be0 100644 --- a/apps/docs/src/guide/types/snippets/address/from-a-b256.ts +++ b/apps/docs/src/guide/types/snippets/address/from-a-b256.ts @@ -5,7 +5,7 @@ const b256 = '0xbebd3baab326f895289ecbd4210cf886ce41952316441ae4cac35f00f0e882a6'; // #endregion b256-1 -const address = Address.fromB256(b256); +const address = new Address(b256); console.log('b256', address.toB256()); // 0xbebd3baab326f895289ecbd4210cf886ce41952316441ae4cac35f00f0e882a6 diff --git a/apps/docs/src/guide/types/snippets/address/from-a-public-key.ts b/apps/docs/src/guide/types/snippets/address/from-a-public-key.ts index 83077dd8ea4..919d8a4b00c 100644 --- a/apps/docs/src/guide/types/snippets/address/from-a-public-key.ts +++ b/apps/docs/src/guide/types/snippets/address/from-a-public-key.ts @@ -7,7 +7,7 @@ const provider = new Provider(LOCAL_NETWORK_URL); const wallet = Wallet.generate({ provider }); -const address = Address.fromPublicKey(wallet.publicKey); +const address = new Address(wallet.publicKey); // #endregion full console.log('address', address); diff --git a/apps/docs/src/guide/types/snippets/address/from-an-evm-address.ts b/apps/docs/src/guide/types/snippets/address/from-an-evm-address.ts new file mode 100644 index 00000000000..e0309cf2859 --- /dev/null +++ b/apps/docs/src/guide/types/snippets/address/from-an-evm-address.ts @@ -0,0 +1,9 @@ +// #region full +import { Address } from 'fuels'; + +const evmAddress = '0x675b68aa4d9c2d3bb3f0397048e62e6b7192079c'; + +const address = new Address(evmAddress); +// #endregion full + +console.log('address', address); diff --git a/apps/docs/src/guide/types/snippets/address/from-an-existing-address.ts b/apps/docs/src/guide/types/snippets/address/from-an-existing-address.ts new file mode 100644 index 00000000000..0a0c4dc454e --- /dev/null +++ b/apps/docs/src/guide/types/snippets/address/from-an-existing-address.ts @@ -0,0 +1,9 @@ +// #region full +import { Address } from 'fuels'; + +const address = Address.fromRandom(); + +const addressClone = new Address(address); +// #endregion full + +console.log('addressCloneFromB256', addressClone); diff --git a/apps/docs/src/guide/types/snippets/address/utilities-function-1.ts b/apps/docs/src/guide/types/snippets/address/utilities-function-1.ts deleted file mode 100644 index 0823a6f9400..00000000000 --- a/apps/docs/src/guide/types/snippets/address/utilities-function-1.ts +++ /dev/null @@ -1,9 +0,0 @@ -// #region full -import { Address } from 'fuels'; - -const address = Address.fromRandom(); - -const addressCloneFromB256 = Address.fromString(address.toB256()); -// #endregion full - -console.log('addressCloneFromB256', addressCloneFromB256); diff --git a/apps/docs/src/guide/types/snippets/address/utilities-function-2.ts b/apps/docs/src/guide/types/snippets/address/utilities-function-2.ts deleted file mode 100644 index 3e6d4ba15fc..00000000000 --- a/apps/docs/src/guide/types/snippets/address/utilities-function-2.ts +++ /dev/null @@ -1,10 +0,0 @@ -// #region full -import { Address } from 'fuels'; - -const dataFromInput: string = - '0xf1e92c42b90934aa6372e30bc568a326f6e66a1a0288595e6e3fbd392a4f3e6e'; - -// If the input string can't be resolved this will throw an error -const address = Address.fromDynamicInput(dataFromInput); -// #endregion full -console.log('address', address); diff --git a/apps/docs/src/guide/types/snippets/address/utilities-function-3.ts b/apps/docs/src/guide/types/snippets/address/utilities-function-equals.ts similarity index 59% rename from apps/docs/src/guide/types/snippets/address/utilities-function-3.ts rename to apps/docs/src/guide/types/snippets/address/utilities-function-equals.ts index bd6609f8675..dadba999c6f 100644 --- a/apps/docs/src/guide/types/snippets/address/utilities-function-3.ts +++ b/apps/docs/src/guide/types/snippets/address/utilities-function-equals.ts @@ -3,8 +3,8 @@ import { Address } from 'fuels'; const address = Address.fromRandom(); -const address1 = Address.fromString(address.toString()); -const address2 = Address.fromString(address.toB256()); +const address1 = new Address(address.toString()); +const address2 = new Address(address.toB256()); console.log('equals', address1.equals(address2)); // true diff --git a/apps/docs/src/guide/types/snippets/address/utilities-function-to-checksum.ts b/apps/docs/src/guide/types/snippets/address/utilities-function-to-checksum.ts new file mode 100644 index 00000000000..2372afba188 --- /dev/null +++ b/apps/docs/src/guide/types/snippets/address/utilities-function-to-checksum.ts @@ -0,0 +1,11 @@ +// #region full +import { Address } from 'fuels'; + +const b256 = + '0xbebd3baab326f895289ecbd4210cf886ce41952316441ae4cac35f00f0e882a6'; + +const address = new Address(b256); + +console.log('checksum', address.toChecksum()); +// true +// #endregion full diff --git a/apps/docs/src/guide/types/snippets/b256/support-from-address-class.ts b/apps/docs/src/guide/types/snippets/b256/support-from-address-class.ts index 7adbc198e63..4085db3c08b 100644 --- a/apps/docs/src/guide/types/snippets/b256/support-from-address-class.ts +++ b/apps/docs/src/guide/types/snippets/b256/support-from-address-class.ts @@ -3,7 +3,7 @@ import { getRandomB256, Address } from 'fuels'; const randomB256: string = getRandomB256(); -const address = Address.fromB256(randomB256); +const address = new Address(randomB256); // #endregion full console.log('address', address); diff --git a/apps/docs/src/guide/types/snippets/evm-address/creating-an-evm.ts b/apps/docs/src/guide/types/snippets/evm-address/creating-an-evm.ts index 4e655c1531d..4da93caceea 100644 --- a/apps/docs/src/guide/types/snippets/evm-address/creating-an-evm.ts +++ b/apps/docs/src/guide/types/snippets/evm-address/creating-an-evm.ts @@ -6,7 +6,7 @@ const b256Address = '0xbebd3baab326f895289ecbd4210cf886ce41952316441ae4cac35f00f0e882a6'; // #endregion snippet-2 -const address = Address.fromB256(b256Address); +const address = new Address(b256Address); const evmAddress = address.toEvmAddress(); diff --git a/apps/docs/src/guide/types/snippets/native-parameters/address.ts b/apps/docs/src/guide/types/snippets/native-parameters/address.ts index dd94bf65de9..a062ba5b34c 100644 --- a/apps/docs/src/guide/types/snippets/native-parameters/address.ts +++ b/apps/docs/src/guide/types/snippets/native-parameters/address.ts @@ -17,7 +17,7 @@ const response1 = await contract.functions.address(addressInput).get(); // #region address-output const addressOutput = response1.value; -const addressFromOutput: Address = Address.fromB256(addressOutput.bits); +const addressFromOutput: Address = new Address(addressOutput.bits); // #endregion address-output console.log('equals', addressFromOutput.equals(address)); diff --git a/apps/docs/src/guide/types/snippets/native-parameters/identity-address.ts b/apps/docs/src/guide/types/snippets/native-parameters/identity-address.ts index bc7bccbade4..1ecfe223521 100644 --- a/apps/docs/src/guide/types/snippets/native-parameters/identity-address.ts +++ b/apps/docs/src/guide/types/snippets/native-parameters/identity-address.ts @@ -24,7 +24,7 @@ const response = await contract.functions.identity(addressIdentityInput).get(); const identityFromOutput: IdentityOutput = response.value; const addressStringFromOutput: AddressOutput = identityFromOutput.Address as AddressOutput; -const addressFromOutput = Address.fromB256(addressStringFromOutput.bits); +const addressFromOutput = new Address(addressStringFromOutput.bits); // #endregion identity-address-output console.log('equals', addressFromOutput.equals(address)); diff --git a/apps/docs/src/guide/utilities/snippets/address-conversion/asset-id.ts b/apps/docs/src/guide/utilities/snippets/address-conversion/asset-id.ts index bf94e3b9982..e1cb72f00fe 100644 --- a/apps/docs/src/guide/utilities/snippets/address-conversion/asset-id.ts +++ b/apps/docs/src/guide/utilities/snippets/address-conversion/asset-id.ts @@ -4,7 +4,7 @@ import { Address } from 'fuels'; const b256: B256Address = '0x6d309766c0f1c6f103d147b287fabecaedd31beb180d45cf1bf7d88397aecc6f'; -const address: Address = Address.fromB256(b256); +const address: Address = new Address(b256); const assetId: AssetId = address.toAssetId(); // { // bits: '0x6d309766c0f1c6f103d147b287fabecaedd31beb180d45cf1bf7d88397aecc6f diff --git a/apps/docs/src/guide/utilities/snippets/address-conversion/contract.ts b/apps/docs/src/guide/utilities/snippets/address-conversion/contract.ts index b6d4fbd8c5a..42583e5faf6 100644 --- a/apps/docs/src/guide/utilities/snippets/address-conversion/contract.ts +++ b/apps/docs/src/guide/utilities/snippets/address-conversion/contract.ts @@ -8,7 +8,7 @@ import { Counter } from '../../../../typegend/contracts'; const provider = new Provider(LOCAL_NETWORK_URL); const contractAbi = Counter.abi; -const contractAddress = Address.fromB256( +const contractAddress = new Address( '0x6d309766c0f1c6f103d147b287fabecaedd31beb180d45cf1bf7d88397aecc6f' ); diff --git a/apps/docs/src/guide/utilities/snippets/address-conversion/wallet.ts b/apps/docs/src/guide/utilities/snippets/address-conversion/wallet.ts index 75ffce97322..890e61b9cf9 100644 --- a/apps/docs/src/guide/utilities/snippets/address-conversion/wallet.ts +++ b/apps/docs/src/guide/utilities/snippets/address-conversion/wallet.ts @@ -6,7 +6,7 @@ import { LOCAL_NETWORK_URL } from '../../../../env'; const provider = new Provider(LOCAL_NETWORK_URL); -const address = Address.fromB256( +const address = new Address( '0x6d309766c0f1c6f103d147b287fabecaedd31beb180d45cf1bf7d88397aecc6f' ); diff --git a/internal/check-imports/src/references.ts b/internal/check-imports/src/references.ts index aa4fbbe098a..9c5ed1c4e15 100644 --- a/internal/check-imports/src/references.ts +++ b/internal/check-imports/src/references.ts @@ -60,7 +60,7 @@ log(runCliAction); * address */ log(Address); -log(Address.fromPublicKey('asdfasdf')); +log(new Address('asdfasdf')); /** * contract diff --git a/packages/account/src/account.ts b/packages/account/src/account.ts index e41050c1b41..1525c67ba90 100644 --- a/packages/account/src/account.ts +++ b/packages/account/src/account.ts @@ -1,5 +1,5 @@ import { UTXO_ID_LEN } from '@fuel-ts/abi-coder'; -import type { WithAddress } from '@fuel-ts/address'; +import type { AddressInput, WithAddress } from '@fuel-ts/address'; import { Address } from '@fuel-ts/address'; import { randomBytes } from '@fuel-ts/crypto'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; @@ -102,11 +102,11 @@ export class Account extends AbstractAccount implements WithAddress { * @param provider - A Provider instance (optional). * @param connector - A FuelConnector instance (optional). */ - constructor(address: string | Address, provider?: Provider, connector?: FuelConnector) { + constructor(address: AddressInput, provider?: Provider, connector?: FuelConnector) { super(); this._provider = provider; this._connector = connector; - this.address = Address.fromDynamicInput(address); + this.address = new Address(address); } /** @@ -408,7 +408,7 @@ export class Account extends AbstractAccount implements WithAddress { addTransfer(request: ScriptTransactionRequest, transferParams: TransferParams) { const { destination, amount, assetId } = transferParams; this.validateTransferAmount(amount); - request.addCoinOutput(Address.fromAddressOrString(destination), amount, assetId); + request.addCoinOutput(new Address(destination), amount, assetId); return request; } @@ -462,7 +462,7 @@ export class Account extends AbstractAccount implements WithAddress { const transferParams = contractTransferParams.map((transferParam) => { const amount = bn(transferParam.amount); - const contractAddress = Address.fromAddressOrString(transferParam.contractId); + const contractAddress = new Address(transferParam.contractId); const assetId = transferParam.assetId ? hexlify(transferParam.assetId) : defaultAssetId; @@ -502,11 +502,11 @@ export class Account extends AbstractAccount implements WithAddress { * @returns A promise that resolves to the transaction response. */ async withdrawToBaseLayer( - recipient: string | Address, + recipient: AddressInput, amount: BigNumberish, txParams: TxParamsType = {} ): Promise { - const recipientAddress = Address.fromAddressOrString(recipient); + const recipientAddress = new Address(recipient); // add recipient and amount to the transaction script code const recipientDataArray = arrayify( '0x'.concat(recipientAddress.toHexString().substring(2).padStart(64, '0')) diff --git a/packages/account/src/predicate/predicate.ts b/packages/account/src/predicate/predicate.ts index 2d422f3497b..e7bb9eec1b6 100644 --- a/packages/account/src/predicate/predicate.ts +++ b/packages/account/src/predicate/predicate.ts @@ -71,7 +71,7 @@ export class Predicate< abi, configurableConstants ); - const address = Address.fromB256(getPredicateRoot(predicateBytes)); + const address = new Address(getPredicateRoot(predicateBytes)); super(address, provider); this.initialBytecode = arrayify(bytecode); diff --git a/packages/account/src/providers/provider.ts b/packages/account/src/providers/provider.ts index 7d7b6849aac..c0b5b4cc702 100644 --- a/packages/account/src/providers/provider.ts +++ b/packages/account/src/providers/provider.ts @@ -1,3 +1,4 @@ +import type { AddressInput } from '@fuel-ts/address'; import { Address } from '@fuel-ts/address'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; import { BN, bn } from '@fuel-ts/math'; @@ -1041,7 +1042,7 @@ export default class Provider { outputVariables += missingOutputVariables.length; transactionRequest.addVariableOutputs(missingOutputVariables.length); missingOutputContractIds.forEach(({ contractId }) => { - transactionRequest.addContractInputAndOutput(Address.fromString(contractId)); + transactionRequest.addContractInputAndOutput(new Address(contractId)); missingContractIds.push(contractId); }); @@ -1128,7 +1129,7 @@ export default class Provider { result.outputVariables += missingOutputVariables.length; request.addVariableOutputs(missingOutputVariables.length); missingOutputContractIds.forEach(({ contractId }) => { - request.addContractInputAndOutput(Address.fromString(contractId)); + request.addContractInputAndOutput(new Address(contractId)); result.missingContractIds.push(contractId); }); const { maxFee } = await this.estimateTxGasAndFee({ @@ -1428,11 +1429,11 @@ export default class Provider { * @returns A promise that resolves to the coins. */ async getCoins( - owner: string | Address, + owner: AddressInput, assetId?: BytesLike, paginationArgs?: CursorPaginationArgs ): Promise { - const ownerAddress = Address.fromAddressOrString(owner); + const ownerAddress = new Address(owner); const { coins: { edges, pageInfo }, } = await this.operations.getCoins({ @@ -1467,11 +1468,11 @@ export default class Provider { * @returns A promise that resolves to the resources. */ async getResourcesToSpend( - owner: string | Address, + owner: AddressInput, quantities: CoinQuantityLike[], excludedIds?: ExcludeResourcesOption ): Promise { - const ownerAddress = Address.fromAddressOrString(owner); + const ownerAddress = new Address(owner); const excludeInput = { messages: excludedIds?.messages?.map((nonce) => hexlify(nonce)) || [], utxos: excludedIds?.utxos?.map((id) => hexlify(id)) || [], @@ -1506,8 +1507,8 @@ export default class Provider { amount: bn(coin.amount), assetId: coin.assetId, daHeight: bn(coin.daHeight), - sender: Address.fromAddressOrString(coin.sender), - recipient: Address.fromAddressOrString(coin.recipient), + sender: new Address(coin.sender), + recipient: new Address(coin.recipient), nonce: coin.nonce, } as MessageCoin; case 'Coin': @@ -1768,7 +1769,7 @@ export default class Provider { assetId: BytesLike ): Promise { const { contractBalance } = await this.operations.getContractBalance({ - contract: Address.fromAddressOrString(contractId).toB256(), + contract: new Address(contractId).toB256(), asset: hexlify(assetId), }); return bn(contractBalance.amount, 10); @@ -1783,12 +1784,12 @@ export default class Provider { */ async getBalance( /** The address to get coins for */ - owner: string | Address, + owner: AddressInput, /** The asset ID of coins to get */ assetId: BytesLike ): Promise { const { balance } = await this.operations.getBalance({ - owner: Address.fromAddressOrString(owner).toB256(), + owner: new Address(owner).toB256(), assetId: hexlify(assetId), }); return bn(balance.amount, 10); @@ -1801,7 +1802,7 @@ export default class Provider { * @param paginationArgs - Pagination arguments (optional). * @returns A promise that resolves to the balances. */ - async getBalances(owner: string | Address): Promise { + async getBalances(owner: AddressInput): Promise { const { balances: { edges }, } = await this.operations.getBalances({ @@ -1810,7 +1811,7 @@ export default class Provider { * but the current Fuel-Core implementation does not support pagination yet. */ first: 10000, - filter: { owner: Address.fromAddressOrString(owner).toB256() }, + filter: { owner: new Address(owner).toB256() }, }); const balances = edges.map(({ node }) => ({ @@ -1829,7 +1830,7 @@ export default class Provider { * @returns A promise that resolves to the messages. */ async getMessages( - address: string | Address, + address: AddressInput, paginationArgs?: CursorPaginationArgs ): Promise { const { @@ -1839,7 +1840,7 @@ export default class Provider { inputArgs: paginationArgs, paginationLimit: RESOURCES_PAGE_SIZE_LIMIT, }), - owner: Address.fromAddressOrString(address).toB256(), + owner: new Address(address).toB256(), }); const messages = edges.map(({ node }) => ({ @@ -1850,8 +1851,8 @@ export default class Provider { amount: bn(node.amount), data: node.data, }), - sender: Address.fromAddressOrString(node.sender), - recipient: Address.fromAddressOrString(node.recipient), + sender: new Address(node.sender), + recipient: new Address(node.recipient), nonce: node.nonce, amount: bn(node.amount), data: InputMessageCoder.decodeData(node.data), @@ -1970,8 +1971,8 @@ export default class Provider { eventInboxRoot: commitBlockHeader.eventInboxRoot, stateTransitionBytecodeVersion: Number(commitBlockHeader.stateTransitionBytecodeVersion), }, - sender: Address.fromAddressOrString(sender), - recipient: Address.fromAddressOrString(recipient), + sender: new Address(sender), + recipient: new Address(recipient), nonce, amount: bn(amount), data, @@ -2102,8 +2103,8 @@ export default class Provider { amount: bn(rawMessage.amount), data: rawMessage.data, }), - sender: Address.fromAddressOrString(rawMessage.sender), - recipient: Address.fromAddressOrString(rawMessage.recipient), + sender: new Address(rawMessage.sender), + recipient: new Address(rawMessage.recipient), nonce, amount: bn(rawMessage.amount), data: InputMessageCoder.decodeData(rawMessage.data), diff --git a/packages/account/src/providers/transaction-request/transaction-request.ts b/packages/account/src/providers/transaction-request/transaction-request.ts index d38eaed6a94..c37802d1b0d 100644 --- a/packages/account/src/providers/transaction-request/transaction-request.ts +++ b/packages/account/src/providers/transaction-request/transaction-request.ts @@ -1,6 +1,6 @@ import { UTXO_ID_LEN } from '@fuel-ts/abi-coder'; import { Address, addressify } from '@fuel-ts/address'; -import type { AddressLike } from '@fuel-ts/address'; +import type { AddressInput, AddressLike } from '@fuel-ts/address'; import { ZeroBytes32 } from '@fuel-ts/address/configs'; import { randomBytes } from '@fuel-ts/crypto'; import { FuelError } from '@fuel-ts/errors'; @@ -264,8 +264,8 @@ export abstract class BaseTransactionRequest implements BaseTransactionRequestLi * @param address - The address to get the coin input witness index for. * @param signature - The signature to update the witness with. */ - updateWitnessByOwner(address: string | Address, signature: BytesLike) { - const ownerAddress = Address.fromAddressOrString(address); + updateWitnessByOwner(address: AddressInput, signature: BytesLike) { + const ownerAddress = new Address(address); const witnessIndex = this.getCoinInputWitnessIndexByOwner(ownerAddress); if (typeof witnessIndex === 'number') { this.updateWitness(witnessIndex, signature); @@ -689,7 +689,7 @@ export abstract class BaseTransactionRequest implements BaseTransactionRequestLi this.inputs.filter(isRequestInputResource).forEach((i) => { const owner = getRequestInputResourceOwner(i); const correspondingInput = inputsToExtractGasUsed.find((x) => - isRequestInputResourceFromOwner(x, Address.fromString(String(owner))) + isRequestInputResourceFromOwner(x, new Address(String(owner))) ); if ( diff --git a/packages/account/src/signer/signer.ts b/packages/account/src/signer/signer.ts index 850a0358736..4da1cf8ce9c 100644 --- a/packages/account/src/signer/signer.ts +++ b/packages/account/src/signer/signer.ts @@ -38,7 +38,7 @@ export class Signer { // Slice(1) removes the encoding scheme from the public key this.publicKey = hexlify(secp256k1.getPublicKey(privateKeyBytes, false).slice(1)); this.compressedPublicKey = hexlify(secp256k1.getPublicKey(privateKeyBytes, true)); - this.address = Address.fromPublicKey(this.publicKey); + this.address = new Address(this.publicKey); } /** @@ -108,7 +108,7 @@ export class Signer { * @returns Address from signature */ static recoverAddress(data: BytesLike, signature: BytesLike): Address { - return Address.fromPublicKey(Signer.recoverPublicKey(data, signature)); + return new Address(Signer.recoverPublicKey(data, signature)); } /** diff --git a/packages/account/src/utils/formatTransferToContractScriptData.ts b/packages/account/src/utils/formatTransferToContractScriptData.ts index 2155a3ad46a..1edaab8e70a 100644 --- a/packages/account/src/utils/formatTransferToContractScriptData.ts +++ b/packages/account/src/utils/formatTransferToContractScriptData.ts @@ -18,11 +18,7 @@ export const formatTransferToContractScriptData = ( return transferParams.reduce((acc, transferParam) => { const { assetId, amount, contractId } = transferParam; const encoded = numberCoder.encode(amount); - const scriptData = concat([ - Address.fromAddressOrString(contractId).toBytes(), - encoded, - arrayify(assetId), - ]); + const scriptData = concat([new Address(contractId).toBytes(), encoded, arrayify(assetId)]); return concat([acc, scriptData]); }, new Uint8Array()); }; diff --git a/packages/account/src/wallet-manager/vaults/mnemonic-vault.ts b/packages/account/src/wallet-manager/vaults/mnemonic-vault.ts index 247090873e8..af5ae73614c 100644 --- a/packages/account/src/wallet-manager/vaults/mnemonic-vault.ts +++ b/packages/account/src/wallet-manager/vaults/mnemonic-vault.ts @@ -1,3 +1,4 @@ +import type { AddressInput } from '@fuel-ts/address'; import { Address } from '@fuel-ts/address'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; @@ -69,9 +70,9 @@ export class MnemonicVault implements Vault { }; } - exportAccount(address: string | Address): string { + exportAccount(address: AddressInput): string { let numberOfAccounts = 0; - const ownerAddress = Address.fromAddressOrString(address); + const ownerAddress = new Address(address); // Look for the account that has the same address do { const wallet = Wallet.fromMnemonic(this.#secret, this.getDerivePath(numberOfAccounts)); @@ -87,7 +88,7 @@ export class MnemonicVault implements Vault { ); } - getWallet(address: string | Address): WalletUnlocked { + getWallet(address: AddressInput): WalletUnlocked { const privateKey = this.exportAccount(address); return Wallet.fromPrivateKey(privateKey); } diff --git a/packages/account/src/wallet-manager/vaults/privatekey-vault.ts b/packages/account/src/wallet-manager/vaults/privatekey-vault.ts index 127153c4146..a51057ac014 100644 --- a/packages/account/src/wallet-manager/vaults/privatekey-vault.ts +++ b/packages/account/src/wallet-manager/vaults/privatekey-vault.ts @@ -1,3 +1,4 @@ +import type { AddressInput } from '@fuel-ts/address'; import { Address } from '@fuel-ts/address'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; @@ -53,8 +54,8 @@ export class PrivateKeyVault implements Vault { return this.getPublicAccount(wallet.privateKey); } - exportAccount(address: string | Address): string { - const ownerAddress = Address.fromAddressOrString(address); + exportAccount(address: AddressInput): string { + const ownerAddress = new Address(address); const privateKey = this.#privateKeys.find((pk) => Wallet.fromPrivateKey(pk).address.equals(ownerAddress) ); diff --git a/packages/account/src/wallet-manager/wallet-manager.ts b/packages/account/src/wallet-manager/wallet-manager.ts index a2e21c51bf4..a3459985e6a 100644 --- a/packages/account/src/wallet-manager/wallet-manager.ts +++ b/packages/account/src/wallet-manager/wallet-manager.ts @@ -1,3 +1,4 @@ +import type { AddressInput } from '@fuel-ts/address'; import { Address } from '@fuel-ts/address'; import type { Keystore } from '@fuel-ts/crypto'; import { encrypt, decrypt } from '@fuel-ts/crypto'; @@ -111,8 +112,8 @@ export class WalletManager extends EventEmitter { /** * Create a Wallet instance for the specific account */ - getWallet(address: string | Address): WalletUnlocked { - const ownerAddress = Address.fromAddressOrString(address); + getWallet(address: AddressInput): WalletUnlocked { + const ownerAddress = new Address(address); const vaultState = this.#vaults.find((vs) => vs.vault.getAccounts().find((a) => a.address.equals(ownerAddress)) ); @@ -124,8 +125,8 @@ export class WalletManager extends EventEmitter { /** * Export specific account privateKey */ - exportPrivateKey(address: string | Address) { - const ownerAddress = Address.fromAddressOrString(address); + exportPrivateKey(address: AddressInput) { + const ownerAddress = new Address(address); assert(!this.#isLocked, ERROR_MESSAGES.wallet_not_unlocked); const vaultState = this.#vaults.find((vs) => vs.vault.getAccounts().find((a) => a.address.equals(ownerAddress)) diff --git a/packages/account/src/wallet/keystore-wallet.ts b/packages/account/src/wallet/keystore-wallet.ts index 7ceb1ef7fce..296398742a9 100644 --- a/packages/account/src/wallet/keystore-wallet.ts +++ b/packages/account/src/wallet/keystore-wallet.ts @@ -1,3 +1,4 @@ +import type { AddressInput } from '@fuel-ts/address'; import { Address } from '@fuel-ts/address'; import { bufferFromString, @@ -57,12 +58,12 @@ export const removeHexPrefix = (hexString: string) => { export async function encryptKeystoreWallet( privateKey: string, - address: string | Address, + address: AddressInput, password: string ): Promise { // Convert the hexlified private key string to a Buffer. const privateKeyBuffer = bufferFromString(removeHexPrefix(privateKey), 'hex'); - const ownerAddress = Address.fromAddressOrString(address); + const ownerAddress = new Address(address); // Generate a random salt. const salt = randomBytes(DEFAULT_KEY_SIZE); diff --git a/packages/account/test/fixtures/messageProof.ts b/packages/account/test/fixtures/messageProof.ts index 6221d9c47f0..b31bb655243 100644 --- a/packages/account/test/fixtures/messageProof.ts +++ b/packages/account/test/fixtures/messageProof.ts @@ -107,8 +107,8 @@ export const MESSAGE_PROOF: MessageProof = { eventInboxRoot: MESSAGE_PROOF_RAW_RESPONSE.commitBlockHeader.eventInboxRoot, messageOutboxRoot: MESSAGE_PROOF_RAW_RESPONSE.commitBlockHeader.messageOutboxRoot, }, - sender: Address.fromAddressOrString(MESSAGE_PROOF_RAW_RESPONSE.sender), - recipient: Address.fromAddressOrString(MESSAGE_PROOF_RAW_RESPONSE.recipient), + sender: new Address(MESSAGE_PROOF_RAW_RESPONSE.sender), + recipient: new Address(MESSAGE_PROOF_RAW_RESPONSE.recipient), nonce: '0xb33895e6fdf23b5a62c92a1d45c71a11579027f9e5c4dda73c26cf140bcd6895', amount: bn(MESSAGE_PROOF_RAW_RESPONSE.amount), data: MESSAGE_PROOF_RAW_RESPONSE.data, diff --git a/packages/address/src/address.test.ts b/packages/address/src/address.test.ts index 0d2a2fcb78c..0efe8d1f51b 100644 --- a/packages/address/src/address.test.ts +++ b/packages/address/src/address.test.ts @@ -23,293 +23,414 @@ const expectedB256Address = '0xf1e92c42b90934aa6372e30bc568a326f6e66a1a0288595e6 * @group node * @group browser */ -describe('Address utils', () => { - test('isB256 (b256)', () => { - const result = utils.isB256(ADDRESS_B256); +describe('Address', () => { + describe('Address utils', () => { + test('isB256 (b256)', () => { + const result = utils.isB256(ADDRESS_B256); - expect(result).toBeTruthy(); - }); - - test('isB256 (invalid chars)', () => { - const result = utils.isB256(`${ADDRESS_B256}/?`); - - expect(result).toBeFalsy(); - }); - - test('isB256 (too long)', () => { - const result = utils.isB256(`${ADDRESS_B256}abc12345`); - - expect(result).toBeFalsy(); - }); - - test('isB256 (too short)', () => { - const result = utils.isB256('0xef86afa9696cf0dc6385e2c407a63d3cb2a9e4a'); - - expect(result).toBeFalsy(); - }); - - test('isB256 (no hex prefix)', () => { - const result = utils.isB256(ADDRESS_B256.slice(2)); - - expect(result).toBeFalsy(); - }); + expect(result).toBeTruthy(); + }); - test('isPublicKey (publicKey)', () => { - const result = utils.isPublicKey(PUBLIC_KEY); + test('isB256 (invalid chars)', () => { + const result = utils.isB256(`${ADDRESS_B256}/?`); - expect(result).toBeTruthy(); - }); + expect(result).toBeFalsy(); + }); - test('isPublicKey (invalid chars)', () => { - const result = utils.isPublicKey(`${PUBLIC_KEY}/?`); + test('isB256 (too long)', () => { + const result = utils.isB256(`${ADDRESS_B256}abc12345`); - expect(result).toBeFalsy(); - }); + expect(result).toBeFalsy(); + }); - test('isPublicKey (too long)', () => { - const result = utils.isPublicKey(`${PUBLIC_KEY}abc12345`); + test('isB256 (too short)', () => { + const result = utils.isB256('0xef86afa9696cf0dc6385e2c407a63d3cb2a9e4a'); - expect(result).toBeFalsy(); - }); + expect(result).toBeFalsy(); + }); - test('isPublicKey (too short)', () => { - const result = utils.isPublicKey('0xef86afa9696cf0dc6385e2c407a63d3cb2a9e4a'); + test('isB256 (no hex prefix)', () => { + const result = utils.isB256(ADDRESS_B256.slice(2)); - expect(result).toBeFalsy(); - }); + expect(result).toBeFalsy(); + }); - test('isPublicKey (no hex prefix)', () => { - const result = utils.isPublicKey(PUBLIC_KEY.slice(2)); + test('isPublicKey (publicKey)', () => { + const result = utils.isPublicKey(PUBLIC_KEY); - expect(result).toBeFalsy(); - }); - - test('isEvmAddress (EvmAddress)', () => { - const result = utils.isEvmAddress(ADDRESS_EVM); + expect(result).toBeTruthy(); + }); - expect(result).toBeTruthy(); - }); - - test('isEvmAddress (invalid chars)', () => { - const result = utils.isEvmAddress(`${ADDRESS_EVM}/?`); - - expect(result).toBeFalsy(); - }); - - test('isEvmAddress (too long)', () => { - const result = utils.isEvmAddress(`${ADDRESS_EVM}abc12345`); - - expect(result).toBeFalsy(); - }); + test('isPublicKey (invalid chars)', () => { + const result = utils.isPublicKey(`${PUBLIC_KEY}/?`); - test('isEvmAddress (too short)', () => { - const result = utils.isEvmAddress('0x123'); + expect(result).toBeFalsy(); + }); - expect(result).toBeFalsy(); - }); + test('isPublicKey (too long)', () => { + const result = utils.isPublicKey(`${PUBLIC_KEY}abc12345`); - test('isEvmAddress (no hex prefix)', () => { - const result = utils.isEvmAddress(ADDRESS_EVM.slice(2)); + expect(result).toBeFalsy(); + }); - expect(result).toBeFalsy(); - }); + test('isPublicKey (too short)', () => { + const result = utils.isPublicKey('0xef86afa9696cf0dc6385e2c407a63d3cb2a9e4a'); - test('clearFirst12BytesFromB256 (b256 to evm b256)', () => { - const result = utils.toB256AddressEvm(ADDRESS_B256); + expect(result).toBeFalsy(); + }); - expect(result).toEqual(ADDRESS_B256_EVM_PADDED); - }); + test('isPublicKey (no hex prefix)', () => { + const result = utils.isPublicKey(PUBLIC_KEY.slice(2)); - test('clearFirst12BytesFromB256 (invalid B256)', async () => { - const invalidB256 = '0x123'; - const expectedError = new FuelError( - FuelError.CODES.PARSE_FAILED, - `Cannot generate EVM Address B256 from: ${invalidB256}.` - ); - await expectToThrowFuelError(() => utils.toB256AddressEvm(invalidB256), expectedError); - }); + expect(result).toBeFalsy(); + }); - test('padFirst12BytesOfEvmAddress (evm Address to b256)', () => { - const result = utils.padFirst12BytesOfEvmAddress(ADDRESS_EVM); + test('isEvmAddress (EvmAddress)', () => { + const result = utils.isEvmAddress(ADDRESS_EVM); - expect(result).toEqual(ADDRESS_B256_EVM_PADDED); - }); + expect(result).toBeTruthy(); + }); - test('padFirst12BytesOfEvmAddress (invalid EVM Address)', async () => { - const invalidEvmAddress = '0x123'; - const expectedError = new FuelError( - FuelError.CODES.INVALID_EVM_ADDRESS, - 'Invalid EVM address format.' - ); - await expectToThrowFuelError( - () => utils.padFirst12BytesOfEvmAddress(invalidEvmAddress), - expectedError - ); - }); -}); + test('isEvmAddress (invalid chars)', () => { + const result = utils.isEvmAddress(`${ADDRESS_EVM}/?`); -describe('Address class', () => { - test('instantiate an Address class', () => { - const result = new Address(ADDRESS_B256.toUpperCase() as B256Address); + expect(result).toBeFalsy(); + }); - expect(result.toAddress()).toEqual(ADDRESS_B256); - expect(result.toString()).toEqual(ADDRESS_CHECKSUM); - expect(`cast as string${result}`).toEqual(`cast as string${ADDRESS_CHECKSUM}`); - expect(result.toB256()).toEqual(ADDRESS_B256); - expect(result.toBytes()).toEqual(new Uint8Array(ADDRESS_BYTES)); - }); + test('isEvmAddress (too long)', () => { + const result = utils.isEvmAddress(`${ADDRESS_EVM}abc12345`); - test('instance equality', () => { - const resultA = new Address(ADDRESS_B256); - const resultB = new Address(ADDRESS_B256.toUpperCase() as B256Address); + expect(result).toBeFalsy(); + }); - expect(resultA).toEqual(resultB); - expect(resultA.equals(resultB)).toBeTruthy(); - expect(resultB.equals(resultA)).toBeTruthy(); - }); + test('isEvmAddress (too short)', () => { + const result = utils.isEvmAddress('0x123'); - test('create an Address class using public key', () => { - const address = Address.fromPublicKey(PUBLIC_KEY); + expect(result).toBeFalsy(); + }); - expect(address.toAddress()).toEqual(expectedB256Address); - expect(address.toB256()).toEqual(expectedB256Address); - }); + test('isEvmAddress (no hex prefix)', () => { + const result = utils.isEvmAddress(ADDRESS_EVM.slice(2)); - test('create an Address class using invalid public key (no hex prefix)', async () => { - const address = PUBLIC_KEY.slice(2); + expect(result).toBeFalsy(); + }); - const expectedError = new FuelError( - FuelError.CODES.INVALID_PUBLIC_KEY, - `Invalid Public Key: ${address}.` - ); - await expectToThrowFuelError(() => Address.fromPublicKey(address), expectedError); - }); + test('clearFirst12BytesFromB256 (b256 to evm b256)', () => { + const result = utils.toB256AddressEvm(ADDRESS_B256); - test('create an Address class using b256Address', () => { - const address = Address.fromB256(ADDRESS_B256); + expect(result).toEqual(ADDRESS_B256_EVM_PADDED); + }); - expect(address.toAddress()).toEqual(ADDRESS_B256); - expect(address.toB256()).toEqual(ADDRESS_B256); - }); + test('clearFirst12BytesFromB256 (invalid B256)', async () => { + const invalidB256 = '0x123'; + const expectedError = new FuelError( + FuelError.CODES.PARSE_FAILED, + `Cannot generate EVM Address B256 from: ${invalidB256}.` + ); + await expectToThrowFuelError(() => utils.toB256AddressEvm(invalidB256), expectedError); + }); - test('create an Address class using invalid b256Address (no hex prefix)', async () => { - const address = ADDRESS_B256.slice(2); + test('padFirst12BytesOfEvmAddress (evm Address to b256)', () => { + const result = utils.padFirst12BytesOfEvmAddress(ADDRESS_EVM); - const expectedError = new FuelError( - FuelError.CODES.INVALID_B256_ADDRESS, - `Invalid B256 Address: ${address}.` - ); - await expectToThrowFuelError(() => Address.fromB256(address), expectedError); - }); + expect(result).toEqual(ADDRESS_B256_EVM_PADDED); + }); - test('when parsing to JSON it should show the b256 address', () => { - const result = Address.fromB256(expectedB256Address); - expect(JSON.stringify(result)).toEqual(`"${expectedB256Address}"`); - }); + test('padFirst12BytesOfEvmAddress (invalid EVM Address)', async () => { + const invalidEvmAddress = '0x123'; + const expectedError = new FuelError( + FuelError.CODES.INVALID_EVM_ADDRESS, + 'Invalid EVM address format.' + ); + await expectToThrowFuelError( + () => utils.padFirst12BytesOfEvmAddress(invalidEvmAddress), + expectedError + ); + }); - test('valueOf matches toString', () => { - const address = new Address(ADDRESS_B256); + test('fromEvmAddressToB256 (evm Address to b256)', () => { + const result = utils.fromEvmAddressToB256(ADDRESS_EVM); - expect(address.toString()).toEqual(address.valueOf()); - }); + expect(result).toEqual(ADDRESS_B256_EVM_PADDED); + }); - test('create an Address class fromDynamicInput [public key]', () => { - const address = Address.fromDynamicInput(PUBLIC_KEY); + test('fromEvmAddressToB256 (invalid EVM Address)', async () => { + const invalidEvmAddress = '0x123'; + const expectedError = new FuelError( + FuelError.CODES.INVALID_EVM_ADDRESS, + 'Invalid EVM address format.' + ); + await expectToThrowFuelError( + () => utils.fromEvmAddressToB256(invalidEvmAddress), + expectedError + ); + }); - expect(address.toAddress()).toEqual(expectedB256Address); - expect(address.toB256()).toEqual(expectedB256Address); - }); + test('fromPublicKeyToB256 (public key to b256)', () => { + const result = utils.fromPublicKeyToB256(PUBLIC_KEY); - test('create an Address class fromDynamicInput [b256Address]', () => { - const address = Address.fromDynamicInput(expectedB256Address); + expect(result).toEqual(expectedB256Address); + }); - expect(address.toAddress()).toEqual(expectedB256Address); - }); + test('fromPublicKeyToB256 (invalid public key)', async () => { + const invalidPublicKey = '0x123'; + const expectedError = new FuelError( + FuelError.CODES.INVALID_PUBLIC_KEY, + `Invalid Public Key: ${invalidPublicKey}.` + ); + await expectToThrowFuelError( + () => utils.fromPublicKeyToB256(invalidPublicKey), + expectedError + ); + }); + + test('fromDynamicInputToB256 [public key]', () => { + const address = utils.fromDynamicInputToB256(PUBLIC_KEY); + + expect(address).toEqual(expectedB256Address); + }); + + test('fromDynamicInputToB256 [b256Address]', () => { + const address = utils.fromDynamicInputToB256(expectedB256Address); + + expect(address).toEqual(expectedB256Address); + }); + + test('fromDynamicInputToB256 [b256Address]', () => { + const address = utils.fromDynamicInputToB256(expectedB256Address); + + expect(address).toEqual(expectedB256Address); + }); + + test('fromDynamicInputToB256 [evmAddress]', () => { + const address = utils.fromDynamicInputToB256(ADDRESS_EVM); + + expect(address).toEqual(ADDRESS_B256_EVM_PADDED); + }); + + test('fromDynamicInputToB256 [bad input]', async () => { + const expectedError = new FuelError( + FuelError.CODES.PARSE_FAILED, + `Unknown address format: only 'B256', 'Public Key (512)', or 'EVM Address' are supported.` + ); + await expectToThrowFuelError(() => utils.fromDynamicInputToB256('badinput'), expectedError); + }); + + test('fromDynamicInputToB256 [Address]', () => { + const address: Address = Address.fromRandom(); + const newAddress: B256Address = utils.fromDynamicInputToB256(address); + + expect(newAddress).toEqual(address.toB256()); + }); + }); + + describe('Address class', () => { + test('instantiate an Address class', () => { + const result = new Address(ADDRESS_B256.toUpperCase() as B256Address); + + expect(result.toAddress()).toEqual(ADDRESS_B256); + expect(result.toString()).toEqual(ADDRESS_CHECKSUM); + expect(`cast as string${result}`).toEqual(`cast as string${ADDRESS_CHECKSUM}`); + expect(result.toB256()).toEqual(ADDRESS_B256); + expect(result.toBytes()).toEqual(new Uint8Array(ADDRESS_BYTES)); + }); + + test('instantiate an Address class [public key]', () => { + const address = new Address(PUBLIC_KEY); + + expect(address.toAddress()).toEqual(expectedB256Address); + expect(address.toB256()).toEqual(expectedB256Address); + }); + + test('instantiate an Address class [b256Address]', () => { + const address = new Address(expectedB256Address); + + expect(address.toAddress()).toEqual(expectedB256Address); + }); - test('create an Address class fromDynamicInput [b256Address]', () => { - const address = Address.fromDynamicInput(expectedB256Address); + test('instantiate an Address class [b256Address]', () => { + const address = new Address(expectedB256Address); - expect(address.toB256()).toEqual(expectedB256Address); - }); + expect(address.toB256()).toEqual(expectedB256Address); + }); - test('create an Address class fromDynamicInput [evmAddress]', () => { - const address = Address.fromDynamicInput(ADDRESS_EVM); + test('instantiate an Address class [evmAddress]', () => { + const address = new Address(ADDRESS_EVM); - expect(address.toB256()).toEqual(ADDRESS_B256_EVM_PADDED); - }); + expect(address.toB256()).toEqual(ADDRESS_B256_EVM_PADDED); + }); - test('create an Address class fromDynamicInput [bad input]', async () => { - const expectedError = new FuelError( - FuelError.CODES.PARSE_FAILED, - `Unknown address format: only 'B256', or 'Public Key (512)' are supported.` - ); - await expectToThrowFuelError(() => Address.fromDynamicInput('badinput'), expectedError); - }); + test('instantiate an Address class [bad input]', async () => { + const expectedError = new FuelError( + FuelError.CODES.PARSE_FAILED, + `Unknown address format: only 'B256', 'Public Key (512)', or 'EVM Address' are supported.` + ); + await expectToThrowFuelError(() => new Address('badinput'), expectedError); + }); - test('create an Address class fromDynamicInput [Address]', () => { - const address = Address.fromRandom(); - const newAddress = Address.fromDynamicInput(address); + test('instantiate an Address class [Address]', () => { + const address = Address.fromRandom(); + const newAddress = new Address(address); - expect(newAddress.toB256()).toEqual(address.toB256()); - expect(address).toBe(address); - expect(newAddress).not.toBe(address); - }); + expect(newAddress.toB256()).toEqual(address.toB256()); + expect(address).toBe(address); + expect(newAddress).not.toBe(address); + }); - test('create an EvmAddress from B256', () => { - const address = Address.fromB256(ADDRESS_B256); - const evmAddress: EvmAddress = address.toEvmAddress(); + test('instance equality', () => { + const resultA = new Address(ADDRESS_B256); + const resultB = new Address(ADDRESS_B256.toUpperCase() as B256Address); - expect(evmAddress).toBeDefined(); - expect(evmAddress.bits).toBe(ADDRESS_B256_EVM_PADDED); - }); + expect(resultA).toEqual(resultB); + expect(resultA.equals(resultB)).toBeTruthy(); + expect(resultB.equals(resultA)).toBeTruthy(); + }); - test('create an AssetId from B256', () => { - const address = Address.fromB256(ADDRESS_B256); - const assetId: AssetId = address.toAssetId(); + test('create an Address class using public key', () => { + const address = Address.fromPublicKey(PUBLIC_KEY); - expect(assetId).toBeDefined(); - expect(assetId.bits).toBe(ADDRESS_B256); - }); + expect(address.toAddress()).toEqual(expectedB256Address); + expect(address.toB256()).toEqual(expectedB256Address); + }); - test('create an Address from an Evm Address', () => { - const address = Address.fromEvmAddress(ADDRESS_EVM); + test('create an Address class using invalid public key (no hex prefix)', async () => { + const address = PUBLIC_KEY.slice(2); - const evmAddressWrapped: EvmAddress = { - bits: ADDRESS_B256_EVM_PADDED, - }; + const expectedError = new FuelError( + FuelError.CODES.INVALID_PUBLIC_KEY, + `Invalid Public Key: ${address}.` + ); + await expectToThrowFuelError(() => Address.fromPublicKey(address), expectedError); + }); - expect(address.toEvmAddress()).toMatchObject(evmAddressWrapped); - expect(address.toB256()).toEqual(ADDRESS_B256_EVM_PADDED); - }); + test('create an Address class using b256Address', () => { + const address = Address.fromB256(ADDRESS_B256); - test('create an Address class using invalid Evm Address (no hex prefix)', async () => { - const address = ADDRESS_EVM.slice(2); + expect(address.toAddress()).toEqual(ADDRESS_B256); + expect(address.toB256()).toEqual(ADDRESS_B256); + }); - const expectedError = new FuelError( - FuelError.CODES.INVALID_EVM_ADDRESS, - `Invalid Evm Address: ${address}.` - ); - await expectToThrowFuelError(() => Address.fromEvmAddress(address), expectedError); - }); + test('create an Address class using invalid b256Address (no hex prefix)', async () => { + const address = ADDRESS_B256.slice(2); - test('validate checksum address', () => { - const address = Address.fromRandom(); - const addressMock = '0x9cfB2CAd509D417ec40b70ebE1DD72a3624D46fdD1Ea5420dBD755CE7f4Dc897'; - expect(Address.isChecksumValid(address.toChecksum())).toBeTruthy(); - expect(Address.fromB256(addressMock).toChecksum()).toBe(addressMock); - expect(Address.isChecksumValid(addressMock)).toBeTruthy(); - expect(Address.isChecksumValid(addressMock.slice(2))).toBeTruthy(); - }); + const expectedError = new FuelError( + FuelError.CODES.INVALID_B256_ADDRESS, + `Invalid B256 Address: ${address}.` + ); + await expectToThrowFuelError(() => Address.fromB256(address), expectedError); + }); - test('validate checksum address for invalid types', () => { - const address = Address.fromRandom(); - expect(Address.isChecksumValid(address.toB256().toLowerCase())).toBeFalsy(); - expect( - Address.isChecksumValid('0x9cfB2CAd509D417ec40b70ebE1DD72a3624D46fdD1Ea5420dBD755CE7f4dc897') - ).toBeFalsy(); - expect( - Address.isChecksumValid('9cFB2Cad509d417eC40B70eBe1DD72a3624d46fDD1EA5420DbD755Ce7f4dc897') - ).toBeFalsy(); - expect(Address.isChecksumValid('9cFB2Cad509d417eC40B70eBe1DD72')).toBeFalsy(); + test('when parsing to JSON it should show the b256 address', () => { + const result = Address.fromB256(expectedB256Address); + expect(JSON.stringify(result)).toEqual(`"${expectedB256Address}"`); + }); + + test('valueOf matches toString', () => { + const address = new Address(ADDRESS_B256); + + expect(address.toString()).toEqual(address.valueOf()); + }); + + test('create an Address class fromDynamicInput [public key]', () => { + const address = Address.fromDynamicInput(PUBLIC_KEY); + + expect(address.toAddress()).toEqual(expectedB256Address); + expect(address.toB256()).toEqual(expectedB256Address); + }); + + test('create an Address class fromDynamicInput [b256Address]', () => { + const address = Address.fromDynamicInput(expectedB256Address); + + expect(address.toAddress()).toEqual(expectedB256Address); + }); + + test('create an Address class fromDynamicInput [b256Address]', () => { + const address = Address.fromDynamicInput(expectedB256Address); + + expect(address.toB256()).toEqual(expectedB256Address); + }); + + test('create an Address class fromDynamicInput [evmAddress]', () => { + const address = Address.fromDynamicInput(ADDRESS_EVM); + + expect(address.toB256()).toEqual(ADDRESS_B256_EVM_PADDED); + }); + + test('create an Address class fromDynamicInput [bad input]', async () => { + const expectedError = new FuelError( + FuelError.CODES.PARSE_FAILED, + `Unknown address format: only 'B256', 'Public Key (512)', or 'EVM Address' are supported.` + ); + await expectToThrowFuelError(() => Address.fromDynamicInput('badinput'), expectedError); + }); + + test('create an Address class fromDynamicInput [Address]', () => { + const address = Address.fromRandom(); + const newAddress = Address.fromDynamicInput(address); + + expect(newAddress.toB256()).toEqual(address.toB256()); + expect(address).toBe(address); + expect(newAddress).not.toBe(address); + }); + + test('create an EvmAddress from B256', () => { + const address = Address.fromB256(ADDRESS_B256); + const evmAddress: EvmAddress = address.toEvmAddress(); + + expect(evmAddress).toBeDefined(); + expect(evmAddress.bits).toBe(ADDRESS_B256_EVM_PADDED); + }); + + test('create an AssetId from B256', () => { + const address = Address.fromB256(ADDRESS_B256); + const assetId: AssetId = address.toAssetId(); + + expect(assetId).toBeDefined(); + expect(assetId.bits).toBe(ADDRESS_B256); + }); + + test('create an Address from an Evm Address', () => { + const address = Address.fromEvmAddress(ADDRESS_EVM); + + const evmAddressWrapped: EvmAddress = { + bits: ADDRESS_B256_EVM_PADDED, + }; + + expect(address.toEvmAddress()).toMatchObject(evmAddressWrapped); + expect(address.toB256()).toEqual(ADDRESS_B256_EVM_PADDED); + }); + + test('create an Address class using invalid Evm Address (no hex prefix)', async () => { + const address = ADDRESS_EVM.slice(2); + + const expectedError = new FuelError( + FuelError.CODES.INVALID_EVM_ADDRESS, + `Invalid Evm Address: ${address}.` + ); + await expectToThrowFuelError(() => Address.fromEvmAddress(address), expectedError); + }); + + test('validate checksum address', () => { + const address = Address.fromRandom(); + const addressMock = '0x9cfB2CAd509D417ec40b70ebE1DD72a3624D46fdD1Ea5420dBD755CE7f4Dc897'; + expect(Address.isChecksumValid(address.toChecksum())).toBeTruthy(); + expect(Address.fromB256(addressMock).toChecksum()).toBe(addressMock); + expect(Address.isChecksumValid(addressMock)).toBeTruthy(); + expect(Address.isChecksumValid(addressMock.slice(2))).toBeTruthy(); + }); + + test('validate checksum address for invalid types', () => { + const address = Address.fromRandom(); + expect(Address.isChecksumValid(address.toB256().toLowerCase())).toBeFalsy(); + expect( + Address.isChecksumValid( + '0x9cfB2CAd509D417ec40b70ebE1DD72a3624D46fdD1Ea5420dBD755CE7f4dc897' + ) + ).toBeFalsy(); + expect( + Address.isChecksumValid('9cFB2Cad509d417eC40B70eBe1DD72a3624d46fDD1EA5420DbD755Ce7f4dc897') + ).toBeFalsy(); + expect(Address.isChecksumValid('9cFB2Cad509d417eC40B70eBe1DD72')).toBeFalsy(); + }); }); }); diff --git a/packages/address/src/address.ts b/packages/address/src/address.ts index e8691e1ea38..bee16489617 100644 --- a/packages/address/src/address.ts +++ b/packages/address/src/address.ts @@ -2,14 +2,14 @@ import { FuelError } from '@fuel-ts/errors'; import { arrayify, hexlify } from '@fuel-ts/utils'; import { sha256 } from '@noble/hashes/sha256'; -import type { B256Address, EvmAddress, AssetId, ChecksumAddress } from './types'; +import type { B256Address, EvmAddress, AssetId, ChecksumAddress, AddressInput } from './types'; import { getRandomB256, - isPublicKey, isB256, isEvmAddress, - padFirst12BytesOfEvmAddress, toB256AddressEvm, + fromPublicKeyToB256, + fromDynamicInputToB256, normalizeB256, } from './utils'; @@ -23,17 +23,11 @@ export class Address { // #endregion address-2 /** - * @param address - A B256 address + * @param address - A B256 address, public key, EVM address, or Address instance */ - constructor(address: B256Address) { - if (!isB256(address)) { - throw new FuelError( - FuelError.CODES.INVALID_B256_ADDRESS, - `Invalid B256 Address: ${address}.` - ); - } - - this.b256Address = normalizeB256(address); + constructor(address: AddressInput) { + const b256Address = fromDynamicInputToB256(address); + this.b256Address = normalizeB256(b256Address); } /** @@ -140,13 +134,11 @@ export class Address { * * @param publicKey - A wallets public key * @returns A new `Address` instance + * + * @deprecated Use `new Address` instead */ static fromPublicKey(publicKey: string): Address { - if (!isPublicKey(publicKey)) { - throw new FuelError(FuelError.CODES.INVALID_PUBLIC_KEY, `Invalid Public Key: ${publicKey}.`); - } - - const b256Address = hexlify(sha256(arrayify(publicKey))); + const b256Address = fromPublicKeyToB256(publicKey); return new Address(b256Address); } @@ -155,6 +147,8 @@ export class Address { * * @param b256Address - A b256 hash * @returns A new `Address` instance + * + * @deprecated Use `new Address` instead */ static fromB256(b256Address: string): Address { if (!isB256(b256Address)) { @@ -173,7 +167,7 @@ export class Address { * @returns A new `Address` instance */ static fromRandom(): Address { - return this.fromB256(getRandomB256()); + return new Address(getRandomB256()); } /** @@ -181,18 +175,22 @@ export class Address { * * @param address - An ambiguous string * @returns A new `Address` instance + * + * @deprecated Use `new Address` instead */ static fromString(address: string): Address { - return this.fromB256(address); + return new Address(address); } /** * Takes an ambiguous string or address and creates an `Address` * * @returns a new `Address` instance + * + * @deprecated Use `new Address` instead */ static fromAddressOrString(address: string | Address): Address { - return typeof address === 'string' ? this.fromString(address) : address; + return new Address(address); } /** @@ -201,36 +199,19 @@ export class Address { * @param addressId - A string containing B256, or Public Key * @throws Error - Unknown address if the format is not recognised * @returns A new `Address` instance + * + * @deprecated Use `new Address` instead */ static fromDynamicInput(address: string | Address): Address { - // If address is a object than we assume it's a Address - // we don't check by instanceof because it's possible to - // the host app to have a different reference to this same class type - if (typeof address !== 'string' && 'toB256' in address) { - return Address.fromB256(address.toB256()); - } - if (isPublicKey(address)) { - return Address.fromPublicKey(address); - } - - if (isB256(address)) { - return Address.fromB256(address); - } - - if (isEvmAddress(address)) { - return Address.fromEvmAddress(address); - } - - throw new FuelError( - FuelError.CODES.PARSE_FAILED, - `Unknown address format: only 'B256', or 'Public Key (512)' are supported.` - ); + return new Address(address); } /** * Takes an Evm Address and returns back an `Address` * * @returns A new `Address` instance + * + * @deprecated Use `new Address` instead */ static fromEvmAddress(evmAddress: string): Address { if (!isEvmAddress(evmAddress)) { @@ -240,8 +221,7 @@ export class Address { ); } - const paddedAddress = padFirst12BytesOfEvmAddress(evmAddress); - return new Address(paddedAddress); + return new Address(evmAddress); } /** diff --git a/packages/address/src/types.ts b/packages/address/src/types.ts index 92f5284f9d2..9241f7a5e2b 100644 --- a/packages/address/src/types.ts +++ b/packages/address/src/types.ts @@ -1,12 +1,7 @@ import type { Address } from './address'; -// #region bech32-1 -/** - * @deprecated - * Type `Bech32Address` is now deprecated. Use `B256` addresses instead. ([help](https://docs.fuel.network/docs/specs/abi/argument-encoding/#b256)) - */ -export type Bech32Address = `fuel${string}`; -// #endregion bech32-1 +export type AddressInput = string | B256Address | Address; + export type B256Address = string; export type ChecksumAddress = string; diff --git a/packages/address/src/utils.ts b/packages/address/src/utils.ts index b7f554ac70e..c1d50598eca 100644 --- a/packages/address/src/utils.ts +++ b/packages/address/src/utils.ts @@ -1,6 +1,7 @@ import { randomBytes } from '@fuel-ts/crypto'; import { FuelError } from '@fuel-ts/errors'; import { arrayify, concat, hexlify } from '@fuel-ts/utils'; +import { sha256 } from '@noble/hashes/sha256'; import type { Address } from './address'; import type { AddressLike, ContractIdLike, B256Address, B256AddressEvm } from './types'; @@ -32,6 +33,14 @@ export function isEvmAddress(address: string): boolean { return address.length === 42 && /(0x)[0-9a-f]{40}$/i.test(address); } +/** + * Normalizes a B256 address to lowercase + * + * @param address - The B256 address to normalize + * @returns The normalized B256 address + * + * @hidden + */ export function normalizeB256(address: B256Address): B256Address { return address.toLowerCase(); } @@ -112,3 +121,61 @@ export const padFirst12BytesOfEvmAddress = (address: string): B256AddressEvm => return address.replace('0x', '0x000000000000000000000000') as B256AddressEvm; }; + +/** + * Converts an EVM address to a B256 address + * + * @param address - The EVM address to convert + * @returns The B256 address + * + * @hidden + */ +export const fromEvmAddressToB256 = (address: string): B256Address => + padFirst12BytesOfEvmAddress(address); + +/** + * Converts a Public Key to a B256 address + * + * @param publicKey - The Public Key to convert + * @returns The B256 address + * + * @hidden + */ +export const fromPublicKeyToB256 = (publicKey: string): B256Address => { + if (!isPublicKey(publicKey)) { + throw new FuelError(FuelError.CODES.INVALID_PUBLIC_KEY, `Invalid Public Key: ${publicKey}.`); + } + + return hexlify(sha256(arrayify(publicKey))); +}; + +/** + * Converts a dynamic input to a B256 address + * + * @param address - The dynamic input to convert + * @returns The B256 address + * + * @hidden + */ +export const fromDynamicInputToB256 = (address: string | Address): B256Address => { + if (typeof address !== 'string' && 'toB256' in address) { + return address.toB256(); + } + + if (isB256(address)) { + return address; + } + + if (isPublicKey(address)) { + return fromPublicKeyToB256(address); + } + + if (isEvmAddress(address)) { + return fromEvmAddressToB256(address); + } + + throw new FuelError( + FuelError.CODES.PARSE_FAILED, + `Unknown address format: only 'B256', 'Public Key (512)', or 'EVM Address' are supported.` + ); +}; diff --git a/packages/program/src/contract.ts b/packages/program/src/contract.ts index 3193944fc04..251e10c309b 100644 --- a/packages/program/src/contract.ts +++ b/packages/program/src/contract.ts @@ -1,6 +1,7 @@ import type { FunctionFragment, JsonAbi } from '@fuel-ts/abi-coder'; import { Interface } from '@fuel-ts/abi-coder'; import type { Account, Provider } from '@fuel-ts/account'; +import type { AddressInput } from '@fuel-ts/address'; import { Address } from '@fuel-ts/address'; import type { BytesLike } from '@fuel-ts/utils'; @@ -44,13 +45,9 @@ export default class Contract implements AbstractContract { * @param abi - The contract's ABI (JSON ABI or Interface instance). * @param accountOrProvider - The account or provider for interaction. */ - constructor( - id: string | Address, - abi: JsonAbi | Interface, - accountOrProvider: Account | Provider - ) { + constructor(id: AddressInput, abi: JsonAbi | Interface, accountOrProvider: Account | Provider) { this.interface = abi instanceof Interface ? abi : new Interface(abi); - this.id = Address.fromAddressOrString(id); + this.id = new Address(id); /** Instead of using `instanceof` to compare classes, we instead check diff --git a/packages/program/src/functions/base-invocation-scope.ts b/packages/program/src/functions/base-invocation-scope.ts index 5e10e131e85..c56f1593d74 100644 --- a/packages/program/src/functions/base-invocation-scope.ts +++ b/packages/program/src/functions/base-invocation-scope.ts @@ -117,7 +117,7 @@ export class BaseInvocationScope { } if (c.externalContractsAbis) { Object.keys(c.externalContractsAbis).forEach((contractId) => - this.transactionRequest.addContractInputAndOutput(Address.fromB256(contractId)) + this.transactionRequest.addContractInputAndOutput(new Address(contractId)) ); } }); @@ -253,7 +253,7 @@ export class BaseInvocationScope { // Adding missing contract ids missingContractIds.forEach((contractId) => { - transactionRequest.addContractInputAndOutput(Address.fromString(contractId)); + transactionRequest.addContractInputAndOutput(new Address(contractId)); }); // Adding required number of OutputVariables @@ -311,7 +311,7 @@ export class BaseInvocationScope { addTransfer(transferParams: TransferParams) { const { amount, destination, assetId } = transferParams; this.transactionRequest = this.transactionRequest.addCoinOutput( - Address.fromAddressOrString(destination), + new Address(destination), amount, assetId ); @@ -328,7 +328,7 @@ export class BaseInvocationScope { addBatchTransfer(transferParams: TransferParams[]) { transferParams.forEach(({ destination, amount, assetId }) => { this.transactionRequest = this.transactionRequest.addCoinOutput( - Address.fromAddressOrString(destination), + new Address(destination), amount, assetId );