diff --git a/.changeset/clever-chefs-shop.md b/.changeset/clever-chefs-shop.md new file mode 100644 index 00000000000..c1853f1bf57 --- /dev/null +++ b/.changeset/clever-chefs-shop.md @@ -0,0 +1,5 @@ +--- +"@fuel-ts/wallet": minor +--- + +Renaming BaseWalletLocked class to Account while abstracting common code. diff --git a/.changeset/honest-ways-learn.md b/.changeset/honest-ways-learn.md new file mode 100644 index 00000000000..448182483d2 --- /dev/null +++ b/.changeset/honest-ways-learn.md @@ -0,0 +1,5 @@ +--- +"fuels": minor +--- + +Adding missing export for package Predicate. diff --git a/.changeset/nine-bottles-jog.md b/.changeset/nine-bottles-jog.md new file mode 100644 index 00000000000..8cb38f6f9f4 --- /dev/null +++ b/.changeset/nine-bottles-jog.md @@ -0,0 +1,10 @@ +--- +"@fuel-ts/address": minor +"@fuel-ts/contract": minor +"@fuel-ts/interfaces": minor +"@fuel-ts/predicate": minor +"@fuel-ts/providers": minor +"@fuel-ts/script": minor +--- + +Updating usages of BaseWalletLocked after renaming it to Account diff --git a/.changeset/pretty-falcons-sleep.md b/.changeset/pretty-falcons-sleep.md new file mode 100644 index 00000000000..4f0db3e454f --- /dev/null +++ b/.changeset/pretty-falcons-sleep.md @@ -0,0 +1,5 @@ +--- +"@fuel-ts/abi-typegen": minor +--- + +Update import from BaseWalletLocked to Account diff --git a/package.json b/package.json index 292531d09ff..8002c90c15f 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "prettier:format": "prettier --write packages", "services:run": "make services-run", "services:clean": "make services-clean", + "services:restart": "run-s services:clean services:run", "changeset:publish": "changeset publish --no-git-tag", "changeset:version-with-docs": "ts-node ./scripts/changeset-version-with-docs", "changeset:next": "ts-node ./scripts/changeset-next", diff --git a/packages/abi-typegen/src/templates/contract/factory.hbs b/packages/abi-typegen/src/templates/contract/factory.hbs index 875c66fa2e6..467c7f57694 100644 --- a/packages/abi-typegen/src/templates/contract/factory.hbs +++ b/packages/abi-typegen/src/templates/contract/factory.hbs @@ -1,7 +1,7 @@ {{header}} import { Interface, Contract } from "fuels"; -import type { Provider, BaseWalletLocked, AbstractAddress } from "fuels"; +import type { Provider, Account, AbstractAddress } from "fuels"; import type { {{capitalizedName}}, {{capitalizedName}}Interface } from "../{{capitalizedName}}"; const _abi = {{abiJsonString}} @@ -13,8 +13,8 @@ export class {{capitalizedName}}__factory { } static connect( id: string | AbstractAddress, - walletOrProvider: BaseWalletLocked | Provider + accountOrProvider: Account | Provider ): {{capitalizedName}} { - return new Contract(id, _abi, walletOrProvider) as unknown as {{capitalizedName}} + return new Contract(id, _abi, accountOrProvider) as unknown as {{capitalizedName}} } } diff --git a/packages/abi-typegen/test/fixtures/templates/contract/factory.hbs b/packages/abi-typegen/test/fixtures/templates/contract/factory.hbs index 6dfbc92dc2f..93f9ab616d3 100644 --- a/packages/abi-typegen/test/fixtures/templates/contract/factory.hbs +++ b/packages/abi-typegen/test/fixtures/templates/contract/factory.hbs @@ -10,7 +10,7 @@ */ import { Interface, Contract } from "fuels"; -import type { Provider, BaseWalletLocked, AbstractAddress } from "fuels"; +import type { Provider, Account, AbstractAddress } from "fuels"; import type { MyContractAbi, MyContractAbiInterface } from "../MyContractAbi"; const _abi = { @@ -61,8 +61,8 @@ export class MyContractAbi__factory { } static connect( id: string | AbstractAddress, - walletOrProvider: BaseWalletLocked | Provider + accountOrProvider: Account | Provider ): MyContractAbi { - return new Contract(id, _abi, walletOrProvider) as unknown as MyContractAbi + return new Contract(id, _abi, accountOrProvider) as unknown as MyContractAbi } } diff --git a/packages/address/package.json b/packages/address/package.json index f86d2fe75f7..dda2ce1281f 100644 --- a/packages/address/package.json +++ b/packages/address/package.json @@ -24,7 +24,6 @@ ], "license": "Apache-2.0", "dependencies": { - "@fuel-ts/constants": "workspace:*", "@fuel-ts/interfaces": "workspace:*", "@fuel-ts/keystore": "workspace:*", "@fuel-ts/versions": "workspace:*", diff --git a/packages/address/src/address.test.ts b/packages/address/src/address.test.ts index 64955f7695b..879fbfc8079 100644 --- a/packages/address/src/address.test.ts +++ b/packages/address/src/address.test.ts @@ -225,4 +225,13 @@ describe('Address class', () => { 'Unknown address format: only Bech32, B256, or Public Key (512) supported' ); }); + + test('create an Address class fromDynamicInput [Address]', async () => { + const address = Address.fromRandom(); + const newAddress = Address.fromDynamicInput(address); + + expect(newAddress.toB256()).toEqual(address.toB256()); + expect(address).toBe(address); + expect(newAddress).not.toBe(address); + }); }); diff --git a/packages/address/src/address.ts b/packages/address/src/address.ts index 610ae6683c2..936101c205d 100644 --- a/packages/address/src/address.ts +++ b/packages/address/src/address.ts @@ -142,17 +142,23 @@ export default class Address extends AbstractAddress { * thrown if the input string is not nilsy and cannot be resolved to a valid address format * @returns a new `Address` instance */ - static fromDynamicInput(addressId: string): Address { - if (isPublicKey(addressId)) { - return Address.fromPublicKey(addressId); + static fromDynamicInput(address: string | AbstractAddress): Address { + // If address is a object than we assume it's a AbstractAddress + // 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 (isBech32(addressId)) { - return new Address(addressId as Bech32Address); + if (isBech32(address)) { + return new Address(address as Bech32Address); } - if (isB256(addressId)) { - return Address.fromB256(addressId); + if (isB256(address)) { + return Address.fromB256(address); } throw new Error('Unknown address format: only Bech32, B256, or Public Key (512) supported'); diff --git a/packages/address/src/utils.ts b/packages/address/src/utils.ts index 8cad4b4be1e..ce54f0de503 100644 --- a/packages/address/src/utils.ts +++ b/packages/address/src/utils.ts @@ -1,7 +1,7 @@ import type { BytesLike } from '@ethersproject/bytes'; import { arrayify, hexlify } from '@ethersproject/bytes'; import { Logger } from '@ethersproject/logger'; -import { AbstractContract, AbstractWallet } from '@fuel-ts/interfaces'; +import { AbstractContract, AbstractAccount } from '@fuel-ts/interfaces'; import type { Bech32Address, B256Address, @@ -91,7 +91,7 @@ export function normalizeBech32(address: Bech32Address): Bech32Address { } export const addressify = (addressLike: AddressLike | ContractIdLike): AbstractAddress => { - if (addressLike instanceof AbstractWallet) { + if (addressLike instanceof AbstractAccount) { return addressLike.address; } diff --git a/packages/contract/src/contracts/contract-factory.ts b/packages/contract/src/contracts/contract-factory.ts index df4a051551b..1ab3f7974e5 100644 --- a/packages/contract/src/contracts/contract-factory.ts +++ b/packages/contract/src/contracts/contract-factory.ts @@ -9,7 +9,7 @@ import { CreateTransactionRequest } from '@fuel-ts/providers'; import type { StorageSlot } from '@fuel-ts/transactions'; import { MAX_GAS_PER_TX } from '@fuel-ts/transactions'; import { versions } from '@fuel-ts/versions'; -import type { BaseWalletLocked } from '@fuel-ts/wallet'; +import type { Account } from '@fuel-ts/wallet'; import { getContractId, getContractStorageRoot, includeHexPrefix } from '../util'; @@ -27,12 +27,12 @@ export default class ContractFactory { bytecode: BytesLike; interface: Interface; provider!: Provider | null; - wallet!: BaseWalletLocked | null; + account!: Account | null; constructor( bytecode: BytesLike, abi: JsonAbi | Interface, - walletOrProvider: BaseWalletLocked | Provider | null = null + accountOrProvider: Account | Provider | null = null ) { // Force the bytecode to be a byte array this.bytecode = arrayify(bytecode); @@ -45,7 +45,7 @@ export default class ContractFactory { /** Instead of using `instanceof` to compare classes, we instead check - if `walletOrProvider` have a `provider` property inside. If yes, + if `accountOrProvider` have a `provider` property inside. If yes, than we assume it's a Wallet. This approach is safer than using `instanceof` because it @@ -56,12 +56,12 @@ export default class ContractFactory { @see Contract */ - if (walletOrProvider && 'provider' in walletOrProvider) { - this.provider = walletOrProvider.provider; - this.wallet = walletOrProvider; + if (accountOrProvider && 'provider' in accountOrProvider) { + this.provider = accountOrProvider.provider; + this.account = accountOrProvider; } else { - this.provider = walletOrProvider; - this.wallet = null; + this.provider = accountOrProvider; + this.account = null; } } @@ -101,19 +101,19 @@ export default class ContractFactory { } async deployContract(deployContractOptions?: DeployContractOptions) { - if (!this.wallet) { + if (!this.account) { return logger.throwArgumentError( - 'Cannot deploy Contract without wallet', - 'wallet', - this.wallet + 'Cannot deploy Contract without account', + 'account', + this.account ); } const { contractId, transactionRequest } = this.createTransactionRequest(deployContractOptions); - await this.wallet.fund(transactionRequest); - const response = await this.wallet.sendTransaction(transactionRequest); + await this.account.fund(transactionRequest); + const response = await this.account.sendTransaction(transactionRequest); await response.wait(); - return new Contract(contractId, this.interface, this.wallet); + return new Contract(contractId, this.interface, this.account); } } diff --git a/packages/contract/src/contracts/contract.test.ts b/packages/contract/src/contracts/contract.test.ts index a8912f75974..7289a6c1462 100644 --- a/packages/contract/src/contracts/contract.test.ts +++ b/packages/contract/src/contracts/contract.test.ts @@ -1,5 +1,5 @@ import { Provider } from '@fuel-ts/providers'; -import { BaseWalletLocked, Wallet } from '@fuel-ts/wallet'; +import { Account, Wallet } from '@fuel-ts/wallet'; import Contract from './contract'; @@ -33,25 +33,25 @@ describe('Contract', () => { const provider = new Provider('http://localhost:4000/graphql'); const contract = new Contract(CONTRACT_ID, ABI, provider); expect(contract.provider).toBe(provider); - expect(contract.wallet).toBe(null); + expect(contract.account).toBe(null); }); test('Create contract instance with wallet', async () => { const wallet = Wallet.generate(); const contract = new Contract(CONTRACT_ID, ABI, wallet); expect(contract.provider).toBe(wallet.provider); - expect(contract.wallet).toBe(wallet); + expect(contract.account).toBe(wallet); }); test('Create contract instance with custom wallet', async () => { const generatedWallet = Wallet.generate(); // Create a custom wallet that extends BaseWalletLocked // but without reference to the BaseWalletLocked class - const BaseWalletLockedCustom = Object.assign(BaseWalletLocked); - expect(BaseWalletLockedCustom).not.toBeInstanceOf(BaseWalletLocked); + const BaseWalletLockedCustom = Object.assign(Account); + expect(BaseWalletLockedCustom).not.toBeInstanceOf(Account); const wallet = new BaseWalletLockedCustom(generatedWallet.address); const contract = new Contract(CONTRACT_ID, ABI, wallet); expect(contract.provider).toBe(wallet.provider); - expect(contract.wallet).toBe(wallet); + expect(contract.account).toBe(wallet); }); }); diff --git a/packages/contract/src/contracts/contract.ts b/packages/contract/src/contracts/contract.ts index f158a1cf844..5a6495004a8 100644 --- a/packages/contract/src/contracts/contract.ts +++ b/packages/contract/src/contracts/contract.ts @@ -4,7 +4,7 @@ import { Interface } from '@fuel-ts/abi-coder'; import { Address } from '@fuel-ts/address'; import type { AbstractAddress, AbstractContract } from '@fuel-ts/interfaces'; import type { Provider } from '@fuel-ts/providers'; -import type { BaseWalletLocked } from '@fuel-ts/wallet'; +import type { Account } from '@fuel-ts/wallet'; import type { InvokeFunctions } from '../types'; @@ -15,20 +15,20 @@ export default class Contract implements AbstractContract { id!: AbstractAddress; provider!: Provider | null; interface!: Interface; - wallet!: BaseWalletLocked | null; + account!: Account | null; functions: InvokeFunctions = {}; constructor( id: string | AbstractAddress, abi: JsonAbi | JsonFlatAbi | Interface, - walletOrProvider: BaseWalletLocked | Provider + accountOrProvider: Account | Provider ) { this.interface = abi instanceof Interface ? abi : new Interface(abi); this.id = Address.fromAddressOrString(id); /** Instead of using `instanceof` to compare classes, we instead check - if `walletOrProvider` have a `provider` property inside. If yes, + if `accountOrProvider` have a `provider` property inside. If yes, than we assume it's a Wallet. This approach is safer than using `instanceof` because it @@ -39,12 +39,12 @@ export default class Contract implements AbstractContract { @see ContractFactory */ - if (walletOrProvider && 'provider' in walletOrProvider) { - this.provider = walletOrProvider.provider; - this.wallet = walletOrProvider; + if (accountOrProvider && 'provider' in accountOrProvider) { + this.provider = accountOrProvider.provider; + this.account = accountOrProvider; } else { - this.provider = walletOrProvider; - this.wallet = null; + this.provider = accountOrProvider; + this.account = null; } Object.keys(this.interface.functions).forEach((name) => { diff --git a/packages/contract/src/contracts/functions/base-invocation-scope.ts b/packages/contract/src/contracts/functions/base-invocation-scope.ts index aa1623cdf6f..7f7458d0098 100644 --- a/packages/contract/src/contracts/functions/base-invocation-scope.ts +++ b/packages/contract/src/contracts/functions/base-invocation-scope.ts @@ -122,7 +122,7 @@ export class BaseInvocationScope { // Add funds required on forwards and to pay gas const opts = BaseInvocationScope.getCallOptions(options); - if (opts.fundTransaction && this.contract.wallet) { + if (opts.fundTransaction && this.contract.account) { await this.fundWithRequiredCoins(); } } @@ -141,7 +141,7 @@ export class BaseInvocationScope { * gasUsed, gasPrice and transaction estimate fee in native coins. */ async getTransactionCost(options?: TransactionCostOptions) { - const provider = (this.contract.wallet?.provider || this.contract.provider) as Provider; + const provider = (this.contract.account?.provider || this.contract.provider) as Provider; assert(provider, 'Wallet or Provider is required!'); await this.prepareTransaction(options); @@ -162,7 +162,7 @@ export class BaseInvocationScope { this.transactionRequest.inputs = this.transactionRequest.inputs.filter( (i) => i.type !== InputType.Coin ); - const resources = await this.contract.wallet?.getResourcesToSpend(this.requiredCoins); + const resources = await this.contract.account?.getResourcesToSpend(this.requiredCoins); this.transactionRequest.addResources(resources || []); return this; } @@ -208,10 +208,10 @@ export class BaseInvocationScope { * running invalid tx and consuming gas try to `simulate` first when possible. */ async call(options?: CallOptions): Promise> { - assert(this.contract.wallet, 'Wallet is required!'); + assert(this.contract.account, 'Wallet is required!'); const transactionRequest = await this.getTransactionRequest(options); - const response = await this.contract.wallet.sendTransaction(transactionRequest); + const response = await this.contract.account.sendTransaction(transactionRequest); return FunctionInvocationResult.build( this.functionInvocationScopes, @@ -229,10 +229,10 @@ export class BaseInvocationScope { * to estimate the amount of gas that will be required to run the transaction. */ async simulate(options?: CallOptions): Promise> { - assert(this.contract.wallet, 'Wallet is required!'); + assert(this.contract.account, 'Wallet is required!'); const transactionRequest = await this.getTransactionRequest(options); - const result = await this.contract.wallet.simulateTransaction(transactionRequest); + const result = await this.contract.account.simulateTransaction(transactionRequest); return InvocationCallResult.build(this.functionInvocationScopes, result, this.isMultiCall); } @@ -246,7 +246,7 @@ export class BaseInvocationScope { * transaction */ async dryRun(options?: CallOptions): Promise> { - const provider = (this.contract.wallet?.provider || this.contract.provider) as Provider; + const provider = (this.contract.account?.provider || this.contract.provider) as Provider; assert(provider, 'Wallet or Provider is required!'); const transactionRequest = await this.getTransactionRequest(options); diff --git a/packages/contract/src/util.ts b/packages/contract/src/util.ts index 5ce9fb800f9..98b5c19512c 100644 --- a/packages/contract/src/util.ts +++ b/packages/contract/src/util.ts @@ -5,14 +5,17 @@ import { calcRoot } from '@fuel-ts/merkle'; import SparseMerkleTree from '@fuel-ts/sparsemerkle'; import type { StorageSlot } from '@fuel-ts/transactions'; -export const getContractRoot = (bytecode: Uint8Array): string => { +export const getContractRoot = (bytecode: BytesLike): string => { const chunkSize = 8; const chunks: Uint8Array[] = []; - for (let offset = 0; offset < bytecode.length; offset += chunkSize) { + const bytes = arrayify(bytecode); + + for (let offset = 0; offset < bytes.length; offset += chunkSize) { const chunk = new Uint8Array(chunkSize); - chunk.set(bytecode.slice(offset, offset + chunkSize)); + chunk.set(bytes.slice(offset, offset + chunkSize)); chunks.push(chunk); } + return calcRoot(chunks.map((c) => hexlify(c))); }; diff --git a/packages/fuel-gauge/src/contract.test.ts b/packages/fuel-gauge/src/contract.test.ts index 6b49d131468..7cd48f4b06a 100644 --- a/packages/fuel-gauge/src/contract.test.ts +++ b/packages/fuel-gauge/src/contract.test.ts @@ -601,7 +601,7 @@ describe('Contract', () => { const transactionRequestParsed = transactionRequestify(txRequestParsed); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const response = await contract.wallet!.sendTransaction(transactionRequestParsed); + const response = await contract.account!.sendTransaction(transactionRequestParsed); const { value: [resultA, resultB], // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -616,7 +616,7 @@ describe('Contract', () => { const wallet = Wallet.generate(); await seedTestWallet(wallet, [ { - amount: bn(1_000_000_000), + amount: bn(1_000_000), assetId: NativeAssetId, }, ]); @@ -628,8 +628,11 @@ describe('Contract', () => { const transactionRequestParsed = transactionRequestify(txRequestParsed); - const response = await contract.wallet?.sendTransaction(transactionRequestParsed); - const result = await response?.waitForResult(); + // Fund tx + await wallet.fund(transactionRequestParsed); + // Send tx + const response = await wallet.sendTransaction(transactionRequestParsed); + const result = await response.waitForResult(); expect(result?.status.type).toBe('success'); }); @@ -638,7 +641,7 @@ describe('Contract', () => { const externalWallet = Wallet.generate(); await seedTestWallet(externalWallet, [ { - amount: bn(1_000_000_000), + amount: bn(1_000_000), assetId: NativeAssetId, }, ]); @@ -660,7 +663,7 @@ describe('Contract', () => { // Set custom provider to contract instance const customProvider = new ProviderCustom('http://127.0.0.1:4000/graphql'); - contract.wallet = Wallet.fromAddress(externalWallet.address, customProvider); + contract.account = Wallet.fromAddress(externalWallet.address, customProvider); contract.provider = customProvider; const num = 1337; @@ -676,7 +679,7 @@ describe('Contract', () => { const transactionRequestParsed = transactionRequestify(txRequestParsed); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const response = await contract.wallet!.sendTransaction(transactionRequestParsed); + const response = await contract.account!.sendTransaction(transactionRequestParsed); const { value: [resultA, resultB], transactionResult, diff --git a/packages/fuel-gauge/src/doc-examples.test.ts b/packages/fuel-gauge/src/doc-examples.test.ts index 2c3b0744a30..cd65a528cc2 100644 --- a/packages/fuel-gauge/src/doc-examples.test.ts +++ b/packages/fuel-gauge/src/doc-examples.test.ts @@ -345,7 +345,7 @@ it('can create a predicate', async () => { // #endregion }); -it.skip('can create a predicate and use', async () => { +it('can create a predicate and use', async () => { // #region typedoc:Predicate-triple-wallets // #context import { Provider, Wallet } from 'fuels'; // #context import { seedTestWallet } from '@fuel-ts/wallet/test-utils'; @@ -359,16 +359,15 @@ it.skip('can create a predicate and use', async () => { const wallet1: WalletUnlocked = Wallet.fromPrivateKey(PRIVATE_KEY_1, provider); const wallet2: WalletUnlocked = Wallet.fromPrivateKey(PRIVATE_KEY_2, provider); const wallet3: WalletUnlocked = Wallet.fromPrivateKey(PRIVATE_KEY_3, provider); - const receiver = Wallet.generate({ provider }); // #endregion // #region typedoc:Predicate-triple-seed // #context import { Provider, Wallet } from 'fuels'; // #context import { seedTestWallet } from '@fuel-ts/wallet/test-utils'; - await seedTestWallet(wallet1, [{ assetId: NativeAssetId, amount: bn(100_000) }]); - await seedTestWallet(wallet2, [{ assetId: NativeAssetId, amount: bn(20_000) }]); - await seedTestWallet(wallet3, [{ assetId: NativeAssetId, amount: bn(30_000) }]); + await seedTestWallet(wallet1, [{ assetId: NativeAssetId, amount: bn(1_000_000) }]); + await seedTestWallet(wallet2, [{ assetId: NativeAssetId, amount: bn(2_000_000) }]); + await seedTestWallet(wallet3, [{ assetId: NativeAssetId, amount: bn(300_000) }]); // #endregion // #region typedoc:Predicate-triple @@ -378,12 +377,20 @@ it.skip('can create a predicate and use', async () => { { typeId: 0, type: 'bool', - components: null, - typeParameters: null, }, { typeId: 1, - type: '[b512; 3]', + type: 'b512', + }, + { + typeId: 2, + type: '[_; 3]', + components: [ + { + name: '__array_element', + type: 1, + }, + ], }, ], functions: [ @@ -391,38 +398,38 @@ it.skip('can create a predicate and use', async () => { inputs: [ { name: 'data', - type: 1, - typeArguments: null, + type: 2, }, ], name: 'main', output: { name: '', type: 0, - typeArguments: null, }, }, ], loggedTypes: [], }; const predicate = new Predicate(predicateTriple, AbiInputs); - const amountToPredicate = 1000; - const assetId = NativeAssetId; - const initialPredicateBalance = await provider.getBalance(predicate.address, assetId); + const amountToPredicate = 100_000; + const amountToReceiver = 100; + const initialPredicateBalance = await predicate.getBalance(); // #endregion // #region typedoc:Predicate-triple-transfer - const response = await wallet1.transfer(predicate.address, amountToPredicate, assetId); - await response.wait(); - const predicateBalance = await provider.getBalance(predicate.address, assetId); + const response = await wallet1.transfer(predicate.address, amountToPredicate); + await response.waitForResult(); + const predicateBalance = await predicate.getBalance(); // assert that predicate address now has the expected amount to predicate expect(bn(predicateBalance)).toEqual(initialPredicateBalance.add(amountToPredicate)); // #endregion // #region typedoc:Predicate-triple-submit - await wallet1.submitPredicate(predicate.address, 200); - const updatedPredicateBalance = await provider.getBalance(predicate.address, assetId); + const depositOnPredicate = await wallet1.transfer(predicate.address, 200); + // Wait for Transaction to succeed + await depositOnPredicate.waitForResult(); + const updatedPredicateBalance = await predicate.getBalance(); // assert that predicate address now has the updated expected amount to predicate expect(bn(updatedPredicateBalance)).toEqual( @@ -440,18 +447,17 @@ it.skip('can create a predicate and use', async () => { // #endregion // #region typedoc:Predicate-triple-spend - await provider.submitSpendPredicate(predicate, updatedPredicateBalance, receiver.address, [ - signatures, - ]); + const tx = await predicate.setData(signatures).transfer(receiver.address, amountToReceiver); + await tx.waitForResult(); // check balances - const finalPredicateBalance = await provider.getBalance(predicate.address, assetId); - const receiverBalance = await provider.getBalance(receiver.address, assetId); + const finalPredicateBalance = await predicate.getBalance(); + const receiverBalance = await receiver.getBalance(); // assert that predicate address now has a zero balance - expect(bn(finalPredicateBalance)).toEqual(bn(0)); + expect(bn(initialPredicateBalance).lte(finalPredicateBalance)).toBeTruthy(); // assert that predicate funds now belong to the receiver - expect(bn(receiverBalance)).toEqual(bn(updatedPredicateBalance)); + expect(bn(receiverBalance).gte(bn(amountToReceiver))).toBeTruthy(); // #endregion }); diff --git a/packages/fuel-gauge/src/predicate.test.ts b/packages/fuel-gauge/src/predicate.test.ts index 6e4d984b11a..e128e3c1727 100644 --- a/packages/fuel-gauge/src/predicate.test.ts +++ b/packages/fuel-gauge/src/predicate.test.ts @@ -1,9 +1,20 @@ import { generateTestWallet } from '@fuel-ts/wallet/test-utils'; import { readFileSync } from 'fs'; -import { Address, NativeAssetId, bn, toHex, toNumber, Provider, Predicate } from 'fuels'; -import type { AbstractAddress, BigNumberish, BN, BaseWalletLocked, BytesLike } from 'fuels'; +import { + Address, + NativeAssetId, + bn, + toHex, + toNumber, + Provider, + Predicate, + Wallet, + Contract, +} from 'fuels'; +import type { BigNumberish, BN, WalletUnlocked, InputValue, WalletLocked } from 'fuels'; import { join } from 'path'; +import contractABIJSON from '../test-projects/call-test-contract/out/debug/call-test-abi.json'; import testPredicateAddress from '../test-projects/predicate-address'; import testPredicateFalse from '../test-projects/predicate-false'; import testPredicateMainArgsStruct from '../test-projects/predicate-main-args-struct'; @@ -12,50 +23,63 @@ import testPredicateStruct from '../test-projects/predicate-struct'; import testPredicateTrue from '../test-projects/predicate-true'; import testPredicateU32 from '../test-projects/predicate-u32'; +import { createSetupConfig } from './utils'; + const testPredicateStructBin = readFileSync( join(__dirname, '../test-projects/predicate-struct/out/debug/predicate-struct.bin') ); +const contractBytecode = readFileSync( + join(__dirname, '../test-projects/call-test-contract/out/debug/call-test.bin') +); + +const setupContract = createSetupConfig({ + contractBytecode, + abi: contractABIJSON, + cache: true, +}); + const setup = async () => { const provider = new Provider('http://127.0.0.1:4000/graphql'); const wallet = await generateTestWallet(provider, [[5_000_000, NativeAssetId]]); - return wallet; + const receiver = Wallet.fromAddress(Address.fromRandom()); + return [wallet, receiver] as const; }; -const setupPredicate = async ( - wallet: BaseWalletLocked, - amountToPredicate: BigNumberish, - predicate: Predicate +const setupPredicate = async ( + wallet: WalletUnlocked, + predicate: Predicate, + amountToPredicate: BigNumberish ): Promise => { - await wallet.submitPredicate(predicate.address, amountToPredicate); - + const tx = await wallet.transfer(predicate.address, amountToPredicate, NativeAssetId); + await tx.waitForResult(); // collect balance from predicate to prevent flaky tests where predicate address gets "filled up" - return wallet.provider.getBalance(predicate.address, NativeAssetId); + return predicate.getBalance(); }; -const assertResults = async ( - wallet: BaseWalletLocked, - receiverAddress: AbstractAddress, +const assertResults = async ( + predicate: Predicate, + receiver: WalletLocked, initialPredicateBalance: BN, initialReceiverBalance: BN, amountToPredicate: BigNumberish, - predicate: Predicate, - isSkippingInitialReceiverBalance = false + amountToReceiver: BigNumberish + // isSkippingInitialReceiverBalance = false ): Promise => { // Check there are UTXO locked with the predicate hash expect(toNumber(initialPredicateBalance)).toBeGreaterThanOrEqual(toNumber(amountToPredicate)); - !isSkippingInitialReceiverBalance && expect(initialReceiverBalance.toHex()).toEqual(toHex(0)); + // !isSkippingInitialReceiverBalance && expect(initialReceiverBalance.toHex()).toEqual(toHex(0)); + expect(initialReceiverBalance.toHex()).toEqual(toHex(0)); // Check the balance of the receiver - const finalReceiverBalance = await wallet.provider.getBalance(receiverAddress, NativeAssetId); - - expect(bn(initialReceiverBalance).add(initialPredicateBalance).toHex()).toEqual( + const finalReceiverBalance = await receiver.getBalance(); + expect(bn(initialReceiverBalance).add(amountToReceiver).toHex()).toEqual( finalReceiverBalance.toHex() ); // Check we spent the entire predicate hash input - const finalPredicateBalance = await wallet.provider.getBalance(predicate.address, NativeAssetId); - expect(finalPredicateBalance.toHex()).toEqual(toHex(0)); + const finalPredicateBalance = await predicate.getBalance(); + expect(finalPredicateBalance.lte(initialPredicateBalance)).toBeTruthy(); }; type Validation = { @@ -183,314 +207,266 @@ const StructAbiInputs = { describe('Predicate', () => { it('can call a no-arg Predicate that returns true', async () => { - const receiverAddress = Address.fromRandom(); - const wallet = await setup(); - const amountToPredicate = 10; - const predicate = new Predicate(testPredicateTrue); - - const initialPredicateBalance = await setupPredicate(wallet, amountToPredicate, predicate); - const initialReceiverBalance = await wallet.provider.getBalance(receiverAddress, NativeAssetId); - - await wallet.provider.submitSpendPredicate(predicate, initialPredicateBalance, receiverAddress); - - await assertResults( - wallet, - receiverAddress, - initialPredicateBalance, - initialReceiverBalance, - amountToPredicate, - predicate - ); - }); - - it('can call a no-arg Predicate that returns true, via wallet', async () => { - const wallet = await setup(); - const amountToPredicate = 10; + const [wallet, receiver] = await setup(); + const amountToPredicate = 100; + const amountToReceiver = 50; const predicate = new Predicate(testPredicateTrue); - const initialPredicateBalance = await setupPredicate(wallet, amountToPredicate, predicate); - const initialReceiverBalance = await wallet.provider.getBalance(wallet.address, NativeAssetId); + const initialReceiverBalance = await receiver.getBalance(); + const initialPredicateBalance = await setupPredicate(wallet, predicate, amountToPredicate); - await wallet.submitSpendPredicate(predicate, initialPredicateBalance); + const tx = await predicate.transfer(receiver.address, amountToReceiver); + await tx.waitForResult(); await assertResults( - wallet, - wallet.address, + predicate, + receiver, initialPredicateBalance, initialReceiverBalance, amountToPredicate, - predicate, - true + amountToReceiver ); }); - it('can call a no-arg Predicate that returns false, via wallet', async () => { - const wallet = await setup(); - const amountToPredicate = 10; + it('can call a no-arg Predicate that returns false', async () => { + const [wallet, receiver] = await setup(); + const amountToPredicate = 100; + const amountToReceiver = 50; const predicate = new Predicate(testPredicateFalse); - const initialPredicateBalance = await setupPredicate(wallet, amountToPredicate, predicate); + await setupPredicate(wallet, predicate, amountToPredicate); await expect(async () => { - await wallet.submitSpendPredicate(predicate, initialPredicateBalance); + await predicate.transfer(receiver.address, amountToReceiver); }).rejects.toThrow('Invalid transaction'); }); it('can call a Coin predicate which returns true with valid predicate data [address]', async () => { - const receiverAddress = Address.fromRandom(); - const wallet = await setup(); - const amountToPredicate = 10; - const predicate = new Predicate(testPredicateAddress, AddressAbiInputs); - - const initialPredicateBalance = await setupPredicate(wallet, amountToPredicate, predicate); - const initialReceiverBalance = await wallet.provider.getBalance(receiverAddress, NativeAssetId); + const [wallet, receiver] = await setup(); + const amountToPredicate = 100; + const amountToReceiver = 50; + const predicate = new Predicate<[string]>(testPredicateAddress, AddressAbiInputs); - await wallet.provider.submitSpendPredicate( - predicate, - initialPredicateBalance, - receiverAddress, - ['0xef86afa9696cf0dc6385e2c407a6e159a1103cefb7e2ae0636fb33d3cb2a9e4a'] - ); - - await assertResults( - wallet, - receiverAddress, - initialPredicateBalance, - initialReceiverBalance, - amountToPredicate, - predicate - ); - }); - - it('can call a Coin predicate which returns true with valid predicate data [address], via wallet', async () => { - const wallet = await setup(); - const amountToPredicate = 10; - const predicate = new Predicate(testPredicateAddress, AddressAbiInputs); - - const initialPredicateBalance = await setupPredicate(wallet, amountToPredicate, predicate); - const initialReceiverBalance = await wallet.provider.getBalance(wallet.address, NativeAssetId); + const initialPredicateBalance = await setupPredicate(wallet, predicate, amountToPredicate); + const initialReceiverBalance = await receiver.getBalance(); - await wallet.submitSpendPredicate(predicate, initialPredicateBalance, [ - '0xef86afa9696cf0dc6385e2c407a6e159a1103cefb7e2ae0636fb33d3cb2a9e4a', - ]); + const tx = await predicate + .setData('0xef86afa9696cf0dc6385e2c407a6e159a1103cefb7e2ae0636fb33d3cb2a9e4a') + .transfer(receiver.address, amountToReceiver); + await tx.waitForResult(); await assertResults( - wallet, - wallet.address, + predicate, + receiver, initialPredicateBalance, initialReceiverBalance, amountToPredicate, - predicate, - true + amountToReceiver ); }); it('can call a Coin predicate which returns false with invalid predicate data [address]', async () => { - const receiverAddress = Address.fromRandom(); - const wallet = await setup(); + const [wallet, receiver] = await setup(); const amountToPredicate = 10; - const predicate = new Predicate(testPredicateAddress, AddressAbiInputs); + const predicate = new Predicate<[string]>(testPredicateAddress, AddressAbiInputs); - const initialPredicateBalance = await setupPredicate(wallet, amountToPredicate, predicate); - const initialReceiverBalance = await wallet.provider.getBalance(receiverAddress, NativeAssetId); + const initialPredicateBalance = await setupPredicate(wallet, predicate, amountToPredicate); + const initialReceiverBalance = await receiver.getBalance(); // Check there are UTXO locked with the predicate hash - expect(toNumber(initialPredicateBalance)).toBeGreaterThanOrEqual(toNumber(amountToPredicate)); + expect(initialPredicateBalance.gte(amountToPredicate)); expect(initialReceiverBalance.toHex()).toEqual(toHex(0)); + predicate.setData('0xbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbada'); + await expect(async () => { - await wallet.provider.submitSpendPredicate( - predicate, - initialPredicateBalance, - receiverAddress, - ['0xbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbada'] - ); + await predicate.transfer(receiver.address, 50); }).rejects.toThrow('Invalid transaction'); }); it('can call a Coin predicate which returns true with valid predicate data [u32]', async () => { - const receiverAddress = Address.fromRandom(); - const wallet = await setup(); - const amountToPredicate = 10; - const predicate = new Predicate(testPredicateU32, U32AbiInputs); + const [wallet, receiver] = await setup(); + const amountToPredicate = 100; + const amountToReceiver = 50; + const predicate = new Predicate<[number]>(testPredicateU32, U32AbiInputs); - const initialPredicateBalance = await setupPredicate(wallet, amountToPredicate, predicate); - const initialReceiverBalance = await wallet.provider.getBalance(receiverAddress, NativeAssetId); + const initialPredicateBalance = await setupPredicate(wallet, predicate, amountToPredicate); + const initialReceiverBalance = await receiver.getBalance(); - await wallet.provider.submitSpendPredicate( - predicate, - initialPredicateBalance, - receiverAddress, - [1078] - ); + const tx = await predicate.setData(1078).transfer(receiver.address, amountToReceiver); + await tx.waitForResult(); await assertResults( - wallet, - receiverAddress, + predicate, + receiver, initialPredicateBalance, initialReceiverBalance, amountToPredicate, - predicate + amountToReceiver ); }); it('can call a Coin predicate which returns false with invalid predicate data [u32]', async () => { - const receiverAddress = Address.fromRandom(); - const wallet = await setup(); + const [wallet, receiver] = await setup(); const amountToPredicate = 10; - const predicate = new Predicate(testPredicateU32, U32AbiInputs); + const predicate = new Predicate<[number]>(testPredicateU32, U32AbiInputs); - const initialPredicateBalance = await setupPredicate(wallet, amountToPredicate, predicate); - const initialReceiverBalance = await wallet.provider.getBalance(receiverAddress, NativeAssetId); + const initialPredicateBalance = await setupPredicate(wallet, predicate, amountToPredicate); + const initialReceiverBalance = await receiver.getBalance(); // Check there are UTXO locked with the predicate hash expect(toNumber(initialPredicateBalance)).toBeGreaterThanOrEqual(amountToPredicate); expect(initialReceiverBalance.toHex()).toEqual(toHex(0)); await expect(async () => { - await wallet.provider.submitSpendPredicate( - predicate, - initialPredicateBalance, - receiverAddress, - [100] - ); + await predicate.setData(100).transfer(receiver.address, amountToPredicate); }).rejects.toThrow('Invalid transaction'); }); it('can call a Coin predicate which returns true with valid predicate data [struct]', async () => { - const receiverAddress = Address.fromRandom(); - const wallet = await setup(); - const amountToPredicate = 10; - const predicate = new Predicate(testPredicateStruct, StructAbiInputs); - - const initialPredicateBalance = await setupPredicate(wallet, amountToPredicate, predicate); - const initialReceiverBalance = await wallet.provider.getBalance(receiverAddress, NativeAssetId); - - const validation: Validation = { - has_account: true, - total_complete: 100, - }; - await wallet.provider.submitSpendPredicate( - predicate, - initialPredicateBalance, - receiverAddress, - [validation] - ); + const [wallet, receiver] = await setup(); + const amountToPredicate = 100; + const amountToReceiver = 50; + const predicate = new Predicate<[Validation]>(testPredicateStruct, StructAbiInputs); + + const initialPredicateBalance = await setupPredicate(wallet, predicate, amountToPredicate); + const initialReceiverBalance = await receiver.getBalance(); + + const tx = await predicate + .setData({ + has_account: true, + total_complete: 100, + }) + .transfer(receiver.address, amountToReceiver); + await tx.waitForResult(); await assertResults( - wallet, - receiverAddress, + predicate, + receiver, initialPredicateBalance, initialReceiverBalance, amountToPredicate, - predicate + amountToReceiver ); }); it('can call a [bin] Coin predicate which returns false with invalid predicate data [struct]', async () => { - const receiverAddress = Address.fromRandom(); - const wallet = await setup(); + const [wallet, receiver] = await setup(); const amountToPredicate = 10; - const predicate = new Predicate(testPredicateStructBin, StructAbiInputs); - const initialPredicateBalance = await setupPredicate(wallet, amountToPredicate, predicate); + const predicate = new Predicate<[Validation]>(testPredicateStructBin, StructAbiInputs); - const validation: Validation = { - has_account: false, - total_complete: 0, - }; + const initialPredicateBalance = await setupPredicate(wallet, predicate, amountToPredicate); + const initialReceiverBalance = await receiver.getBalance(); + + // Check there are UTXO locked with the predicate hash + expect(toNumber(initialPredicateBalance)).toBeGreaterThanOrEqual(amountToPredicate); + expect(initialReceiverBalance.toHex()).toEqual(toHex(0)); await expect(async () => { - await wallet.provider.submitSpendPredicate( - predicate, - initialPredicateBalance, - receiverAddress, - [validation] - ); + await predicate + .setData({ + has_account: false, + total_complete: 0, + }) + .transfer(receiver.address, amountToPredicate); }).rejects.toThrow('Invalid transaction'); }); - // TODO: Enable this test once predicates start to consume gas - // FUELS-TS - https://github.com/FuelLabs/fuels-ts/issues/385 - // SPEC - https://github.com/FuelLabs/fuel-specs/issues/119 - it.skip('should fail if inform gasLimit too low', async () => { - const receiverAddress = Address.fromRandom(); - const wallet = await setup(); - const amountToPredicate = 10; - const predicate = new Predicate(testPredicateStruct, StructAbiInputs); - - const initialPredicateBalance = await setupPredicate(wallet, amountToPredicate, predicate); - - const validation: Validation = { - has_account: true, - total_complete: 100, - }; - - let failed; - try { - await wallet.provider.submitSpendPredicate( - predicate, - initialPredicateBalance, - receiverAddress, - [validation], - undefined, - { gasLimit: 1 } - ); - } catch (e) { - failed = true; - } - - expect(failed).toEqual(true); - }); - it('can call a Coin predicate which returns true with valid predicate data [main args struct]', async () => { - const receiverAddress = Address.fromRandom(); - const wallet = await setup(); - const amountToPredicate = 10; - const predicate = new Predicate(testPredicateStruct, predicateMainArgsStructAbi); - - const initialPredicateBalance = await setupPredicate(wallet, amountToPredicate, predicate); - const initialReceiverBalance = await wallet.provider.getBalance(receiverAddress, NativeAssetId); - - const validation: Validation = { - has_account: true, - total_complete: 100, - }; - await wallet.provider.submitSpendPredicate( - predicate, - initialPredicateBalance, - receiverAddress, - [validation] - ); + const [wallet, receiver] = await setup(); + const amountToPredicate = 100; + const amountToReceiver = 50; + const predicate = new Predicate<[Validation]>(testPredicateStruct, predicateMainArgsStructAbi); + + const initialPredicateBalance = await setupPredicate(wallet, predicate, amountToPredicate); + const initialReceiverBalance = await receiver.getBalance(); + + const tx = await predicate + .setData({ + has_account: true, + total_complete: 100, + }) + .transfer(receiver.address, amountToReceiver); + await tx.waitForResult(); await assertResults( - wallet, - receiverAddress, + predicate, + receiver, initialPredicateBalance, initialReceiverBalance, amountToPredicate, - predicate + amountToReceiver ); }); it('can call a [bin] Coin predicate which returns false with invalid predicate data [main args struct]', async () => { - const receiverAddress = Address.fromRandom(); - const wallet = await setup(); - const amountToPredicate = 10; - const predicate = new Predicate(testPredicateMainArgsStruct, predicateMainArgsStructAbi); - const initialPredicateBalance = await setupPredicate(wallet, amountToPredicate, predicate); + const [wallet, receiver] = await setup(); + const amountToPredicate = 100; + const predicate = new Predicate<[Validation]>( + testPredicateMainArgsStruct, + predicateMainArgsStructAbi + ); - const validation: Validation = { - has_account: false, - total_complete: 0, - }; + const initialPredicateBalance = await setupPredicate(wallet, predicate, amountToPredicate); + + // Check there are UTXO locked with the predicate hash + expect(toNumber(initialPredicateBalance)).toBeGreaterThanOrEqual(amountToPredicate); await expect(async () => { - await wallet.provider.submitSpendPredicate( - predicate, - initialPredicateBalance, - receiverAddress, - [validation] - ); + await predicate + .setData({ + has_account: false, + total_complete: 0, + }) + .transfer(receiver.address, 50); }).rejects.toThrow('Invalid transaction'); }); + + it('should fail if inform gasLimit too low', async () => { + const [wallet, receiver] = await setup(); + const amountToPredicate = 100; + const predicate = new Predicate<[Validation]>(testPredicateStruct, predicateMainArgsStructAbi); + + const predicateBalance = await setupPredicate(wallet, predicate, amountToPredicate); + + const validation: Validation = { + has_account: true, + total_complete: 100, + }; + + // Should throw if not have resouces to pay tx + gasFee + expect(async () => { + await predicate.setData(validation).transfer(receiver.address, predicateBalance); + }).rejects.toThrow(/not enough resources to fit the target/i); + + // Should throw if gasLimit is too low + // TODO: When gas is to low the return error is Invalid transaction, once is fixed on the + // fuel-client we should change with the proper error message + expect(async () => { + await predicate.setData(validation).transfer(receiver.address, 50, NativeAssetId, { + gasLimit: 1, + }); + }).rejects.toThrow(/Invalid transaction/i); + }); + + it('Should be able to use a Predicate to call a contract', async () => { + const [wallet] = await setup(); + const contract = await setupContract(); + const amountToPredicate = 100_000; + const predicate = new Predicate<[Validation]>(testPredicateTrue, predicateMainArgsStructAbi); + // Create a instance of the contract with the predicate as the caller Account + const contractPredicate = new Contract(contract.id, contract.interface, predicate); + const predicateBalance = await setupPredicate(wallet, predicate, amountToPredicate); + + const { value } = await contractPredicate.functions + .return_context_amount() + .callParams({ + forward: [500, NativeAssetId], + }) + .call(); + expect(value.toString()).toEqual('500'); + + const finalPredicateBalance = await predicate.getBalance(); + expect(finalPredicateBalance.lt(predicateBalance)).toBeTruthy(); + }); }); diff --git a/packages/fuel-gauge/test-projects/predicate-triple-sig/src/main.sw b/packages/fuel-gauge/test-projects/predicate-triple-sig/src/main.sw index b4f98c6cefe..f3dd807b68a 100644 --- a/packages/fuel-gauge/test-projects/predicate-triple-sig/src/main.sw +++ b/packages/fuel-gauge/test-projects/predicate-triple-sig/src/main.sw @@ -1,10 +1,11 @@ // #region typedoc:Predicate-triple predicate; -use std::{b512::B512, constants::ZERO_B256, ecr::ec_recover_address, inputs::input_predicate_data}; +use std::{b512::B512, ecr::ec_recover_address, inputs::input_predicate_data}; fn extract_pulic_key_and_match(signature: B512, expected_public_key: b256) -> u64 { - if let Result::Ok(pub_key_sig) = ec_recover_address(signature, ZERO_B256) + let message_hash = 0x6aed34e6bddff5e1d872b5d7d5698a7b73abd6f3b33402732edc73ab9ffb9c70; + if let Result::Ok(pub_key_sig) = ec_recover_address(signature, message_hash) { if pub_key_sig.value == expected_public_key { return 1; @@ -17,9 +18,9 @@ fn main() -> bool { let signatures: [B512; 3] = input_predicate_data(0); let public_keys = [ - 0xd58573593432a30a800f97ad32f877425c223a9e427ab557aab5d5bb89156db0, - 0x14df7c7e4e662db31fe2763b1734a3d680e7b743516319a49baaa22b2032a857, - 0x3ff494fb136978c3125844625dad6baf6e87cdb1328c8a51f35bda5afe72425c, + 0xe10f526b192593793b7a1559a391445faba82a1d669e3eb2dcd17f9c121b24b1, + 0x54944e5b8189827e470e5a8bacfc6c3667397dc4e1eef7ef3519d16d6d6c6610, + 0x577e424ee53a16e6a85291feabc8443862495f74ac39a706d2dd0b9fc16955eb, ]; let mut matched_keys = 0; diff --git a/packages/fuels/src/index.test.ts b/packages/fuels/src/index.test.ts index 20746d4371a..839fc539c21 100644 --- a/packages/fuels/src/index.test.ts +++ b/packages/fuels/src/index.test.ts @@ -6,6 +6,7 @@ describe('index.js', () => { expect(fuels.Address).toBeTruthy(); expect(fuels.Contract).toBeTruthy(); expect(fuels.Predicate).toBeTruthy(); + expect(fuels.Account).toBeTruthy(); expect(fuels.Provider).toBeTruthy(); expect(fuels.Wallet).toBeTruthy(); expect(fuels.TransactionType).toBeTruthy(); diff --git a/packages/fuels/src/index.ts b/packages/fuels/src/index.ts index a7972614e29..a6ff9c2050a 100644 --- a/packages/fuels/src/index.ts +++ b/packages/fuels/src/index.ts @@ -3,6 +3,7 @@ export * from '@fuel-ts/abi-coder'; export * from '@fuel-ts/address'; export * from '@fuel-ts/constants'; export * from '@fuel-ts/contract'; +export * from '@fuel-ts/predicate'; export * from '@fuel-ts/hasher'; export * from '@fuel-ts/interfaces'; export * from '@fuel-ts/keystore'; diff --git a/packages/interfaces/src/index.ts b/packages/interfaces/src/index.ts index de4617df282..3c771be3435 100644 --- a/packages/interfaces/src/index.ts +++ b/packages/interfaces/src/index.ts @@ -25,17 +25,18 @@ export abstract class AbstractContract { abstract id: AbstractAddress; } -export abstract class AbstractWallet { +export abstract class AbstractAccount { abstract address: AbstractAddress; } -export type AddressLike = AbstractAddress | AbstractWallet; +export type AddressLike = AbstractAddress | AbstractAccount; export type ContractIdLike = AbstractAddress | AbstractContract; export abstract class AbstractPredicate { abstract bytes: Uint8Array; abstract address: AbstractAddress; + abstract predicateData: Uint8Array; // eslint-disable-next-line @typescript-eslint/no-explicit-any abstract types?: ReadonlyArray; } diff --git a/packages/predicate/package.json b/packages/predicate/package.json index 4cb0ee94f46..3f97f500000 100644 --- a/packages/predicate/package.json +++ b/packages/predicate/package.json @@ -29,8 +29,13 @@ "@fuel-ts/abi-coder": "workspace:*", "@fuel-ts/address": "workspace:*", "@fuel-ts/interfaces": "workspace:*", - "@fuel-ts/contract": "workspace:*", - "@fuel-ts/versions": "workspace:*" + "@fuel-ts/versions": "workspace:*", + "@fuel-ts/wallet": "workspace:*", + "@fuel-ts/providers": "workspace:*", + "@fuel-ts/transactions": "workspace:*", + "@fuel-ts/merkle": "workspace:*" }, - "devDependencies": {} + "devDependencies": { + "@fuel-ts/math": "workspace:*" + } } diff --git a/packages/predicate/src/predicate.test.ts b/packages/predicate/src/predicate.test.ts new file mode 100644 index 00000000000..2b73f26f83b --- /dev/null +++ b/packages/predicate/src/predicate.test.ts @@ -0,0 +1,123 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { hexlify } from '@ethersproject/bytes'; +import { Address } from '@fuel-ts/address'; +import { bn } from '@fuel-ts/math'; +import { ScriptTransactionRequest, CoinStatus } from '@fuel-ts/providers'; +import type { InputCoin } from '@fuel-ts/transactions'; +import { Account } from '@fuel-ts/wallet'; + +import { Predicate } from './predicate'; + +// This data is arbitrary, make to unit test the Predicate class +const PREDICATE_BYTECODE = + '0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'; +const PREDICATE_ADDRESS = '0x27c54187841c60ac0ebafcf1f2778d30f9973865f9a4bcd959e137aa852c4375'; +const PREDICATE_ABI = { + types: [ + { + typeId: 0, + type: 'bool', + components: null, + typeParameters: null, + }, + { + typeId: 1, + type: 'b256', + }, + ], + functions: [ + { + inputs: [ + { + name: 'data', + type: 1, + typeArguments: null, + }, + ], + name: 'main', + output: { + name: '', + type: 0, + typeArguments: null, + }, + }, + ], + loggedTypes: [], +}; + +describe('Predicate', () => { + it('Should create the correct address for a given bytecode', () => { + const predicate = new Predicate(PREDICATE_BYTECODE); + expect(predicate.address.toB256()).toEqual(PREDICATE_ADDRESS); + }); + + it('Should assign only correct data to predicate', () => { + const predicate = new Predicate(PREDICATE_BYTECODE, PREDICATE_ABI); + const b256 = '0x0101010101010101010101010101010101010101010101010101010101010101'; + + predicate.setData<[string]>(b256); + + // Assign correct data to predicate + expect(hexlify(predicate.predicateData)).toEqual(b256); + // Try to assign incorrect data should fail predicate + expect(async () => { + predicate.setData<[string]>('0x01'); + }).rejects.toThrow(/Invalid b256/i); + }); + + it('Should include predicate on input when sendTransaction', () => { + const b256 = '0x0101010101010101010101010101010101010101010101010101010101010101'; + const sendTransactionMock = jest + .spyOn(Account.prototype, 'sendTransaction') + .mockImplementation(); + const predicate = new Predicate(PREDICATE_BYTECODE, PREDICATE_ABI); + + predicate.setData<[string]>(b256); + + const request = new ScriptTransactionRequest(); + + request.addResource({ + id: '0x01', + assetId: '0x0000000000000000000000000000000000000000000000000000000000000000', + amount: bn(1), + owner: Address.fromB256(PREDICATE_ADDRESS), + status: CoinStatus.Unspent, + maturity: 0, + blockCreated: bn(0), + }); + + predicate.sendTransaction(request); + + const inputCoinMock = sendTransactionMock.mock.calls[0][0].inputs?.[0] as any as InputCoin; + expect(hexlify(inputCoinMock.predicate)).toBe(PREDICATE_BYTECODE); + expect(hexlify(inputCoinMock.predicateData)).toBe(b256); + }); + + it('Should include predicate on input when simulateTransaction', () => { + const b256 = '0x0101010101010101010101010101010101010101010101010101010101010101'; + const simulateTransactionMock = jest + .spyOn(Account.prototype, 'simulateTransaction') + .mockImplementation(); + const predicate = new Predicate(PREDICATE_BYTECODE, PREDICATE_ABI); + + predicate.setData<[string]>(b256); + + const request = new ScriptTransactionRequest(); + + request.addResource({ + id: '0x01', + assetId: '0x0000000000000000000000000000000000000000000000000000000000000000', + amount: bn(1), + owner: Address.fromB256(PREDICATE_ADDRESS), + status: CoinStatus.Unspent, + maturity: 0, + blockCreated: bn(0), + }); + + predicate.simulateTransaction(request); + + const inputCoinMock = simulateTransactionMock.mock.calls[0][0].inputs?.[0] as any as InputCoin; + expect(hexlify(inputCoinMock.predicate)).toBe(PREDICATE_BYTECODE); + expect(hexlify(inputCoinMock.predicateData)).toBe(b256); + }); +}); diff --git a/packages/predicate/src/predicate.ts b/packages/predicate/src/predicate.ts index 9d9b4fcfc8f..cafa00463bb 100644 --- a/packages/predicate/src/predicate.ts +++ b/packages/predicate/src/predicate.ts @@ -1,26 +1,36 @@ import type { BytesLike } from '@ethersproject/bytes'; -import { arrayify } from '@ethersproject/bytes'; +import { hexlify, arrayify } from '@ethersproject/bytes'; import { Logger } from '@ethersproject/logger'; -import { Interface } from '@fuel-ts/abi-coder'; -import type { JsonAbiFragmentType, JsonAbi } from '@fuel-ts/abi-coder'; +import { AbiCoder, Interface } from '@fuel-ts/abi-coder'; +import type { JsonAbiFragmentType, JsonAbi, InputValue } from '@fuel-ts/abi-coder'; import { Address } from '@fuel-ts/address'; -import { ContractUtils } from '@fuel-ts/contract'; -import { AbstractPredicate } from '@fuel-ts/interfaces'; -import type { AbstractAddress } from '@fuel-ts/interfaces'; +import type { + CallResult, + Provider, + TransactionRequestLike, + TransactionResponse, +} from '@fuel-ts/providers'; +import { transactionRequestify } from '@fuel-ts/providers'; +import { InputType } from '@fuel-ts/transactions'; import { versions } from '@fuel-ts/versions'; +import { Account } from '@fuel-ts/wallet'; + +import { getContractRoot } from './utils'; const logger = new Logger(versions.FUELS); -export class Predicate extends AbstractPredicate { +export class Predicate extends Account { bytes: Uint8Array; - address: AbstractAddress; types?: ReadonlyArray; + predicateData: Uint8Array = Uint8Array.from([]); interface?: Interface; - constructor(bytes: BytesLike, types?: JsonAbi) { - super(); + constructor(bytes: BytesLike, types?: JsonAbi, provider?: string | Provider) { + const address = Address.fromB256(getContractRoot(bytes)); + super(address, provider); + + // Assign bytes data this.bytes = arrayify(bytes); - this.address = Address.fromB256(ContractUtils.getContractRoot(this.bytes)); if (types) { this.interface = new Interface(types as JsonAbi); @@ -36,4 +46,36 @@ export class Predicate extends AbstractPredicate { } } } + + populateTransactionPredicateData(transactionRequestLike: TransactionRequestLike) { + const request = transactionRequestify(transactionRequestLike); + + request.inputs?.forEach((input) => { + if (input.type === InputType.Coin && hexlify(input.owner) === this.address.toB256()) { + // eslint-disable-next-line no-param-reassign + input.predicate = this.bytes; + // eslint-disable-next-line no-param-reassign + input.predicateData = this.predicateData; + } + }); + + return request; + } + + sendTransaction(transactionRequestLike: TransactionRequestLike): Promise { + const transactionRequest = this.populateTransactionPredicateData(transactionRequestLike); + return super.sendTransaction(transactionRequest); + } + + simulateTransaction(transactionRequestLike: TransactionRequestLike): Promise { + const transactionRequest = this.populateTransactionPredicateData(transactionRequestLike); + return super.simulateTransaction(transactionRequest); + } + + setData(...args: T) { + const abiCoder = new AbiCoder(); + const encoded = abiCoder.encode(this.types || [], args); + this.predicateData = encoded; + return this; + } } diff --git a/packages/predicate/src/utils/getContractRoot.ts b/packages/predicate/src/utils/getContractRoot.ts new file mode 100644 index 00000000000..956428600d4 --- /dev/null +++ b/packages/predicate/src/utils/getContractRoot.ts @@ -0,0 +1,17 @@ +import type { BytesLike } from '@ethersproject/bytes'; +import { hexlify, arrayify } from '@ethersproject/bytes'; +import { calcRoot } from '@fuel-ts/merkle'; + +export const getContractRoot = (bytecode: BytesLike): string => { + const chunkSize = 8; + const chunks: Uint8Array[] = []; + const bytes = arrayify(bytecode); + + for (let offset = 0; offset < bytes.length; offset += chunkSize) { + const chunk = new Uint8Array(chunkSize); + chunk.set(bytes.slice(offset, offset + chunkSize)); + chunks.push(chunk); + } + + return calcRoot(chunks.map((c) => hexlify(c))); +}; diff --git a/packages/predicate/src/utils/index.ts b/packages/predicate/src/utils/index.ts new file mode 100644 index 00000000000..a1cb82f4415 --- /dev/null +++ b/packages/predicate/src/utils/index.ts @@ -0,0 +1 @@ +export * from './getContractRoot'; diff --git a/packages/providers/src/provider.test.ts b/packages/providers/src/provider.test.ts index fa84fc84187..150b2d5125d 100644 --- a/packages/providers/src/provider.test.ts +++ b/packages/providers/src/provider.test.ts @@ -18,7 +18,7 @@ describe('Provider', () => { const version = await provider.getVersion(); - expect(version).toEqual('0.17.1'); + expect(version).toEqual('0.17.2'); }); it('can call()', async () => { diff --git a/packages/providers/src/provider.ts b/packages/providers/src/provider.ts index f34bd68ea1e..9745589f8aa 100644 --- a/packages/providers/src/provider.ts +++ b/packages/providers/src/provider.ts @@ -2,12 +2,9 @@ import type { BytesLike } from '@ethersproject/bytes'; import { arrayify, hexlify } from '@ethersproject/bytes'; import type { Network } from '@ethersproject/networks'; -import type { InputValue } from '@fuel-ts/abi-coder'; -import { AbiCoder } from '@fuel-ts/abi-coder'; import { Address } from '@fuel-ts/address'; -import { NativeAssetId } from '@fuel-ts/constants'; -import type { AbstractAddress, AbstractPredicate } from '@fuel-ts/interfaces'; -import type { BigNumberish, BN } from '@fuel-ts/math'; +import type { AbstractAddress } from '@fuel-ts/interfaces'; +import type { BN } from '@fuel-ts/math'; import { max, bn } from '@fuel-ts/math'; import type { Transaction } from '@fuel-ts/transactions'; import { @@ -33,12 +30,9 @@ import { coinQuantityfy } from './coin-quantity'; import type { Message, MessageProof } from './message'; import type { ExcludeResourcesOption, Resource } from './resource'; import { isRawCoin } from './resource'; -import { ScriptTransactionRequest, transactionRequestify } from './transaction-request'; +import { transactionRequestify } from './transaction-request'; import type { TransactionRequestLike, TransactionRequest } from './transaction-request'; -import type { - TransactionResult, - TransactionResultReceipt, -} from './transaction-response/transaction-response'; +import type { TransactionResultReceipt } from './transaction-response/transaction-response'; import { TransactionResponse } from './transaction-response/transaction-response'; import { calculateTransactionFee, getReceiptsWithMissingData } from './utils'; @@ -740,95 +734,4 @@ export default class Provider { }, }; } - - async buildSpendPredicate( - predicate: AbstractPredicate, - amountToSpend: BigNumberish, - receiverAddress: AbstractAddress, - predicateData?: InputValue[], - assetId: BytesLike = NativeAssetId, - predicateOptions?: BuildPredicateOptions, - walletAddress?: AbstractAddress - ): Promise { - const predicateResources: Resource[] = await this.getResourcesToSpend(predicate.address, [ - [amountToSpend, assetId], - ]); - const options = { - fundTransaction: true, - ...predicateOptions, - }; - const request = new ScriptTransactionRequest({ - gasLimit: MAX_GAS_PER_TX, - ...options, - }); - - let encoded: undefined | Uint8Array; - if (predicateData && predicate.types) { - const abiCoder = new AbiCoder(); - encoded = abiCoder.encode(predicate.types, predicateData as InputValue[]); - } - - const totalInPredicate: BN = predicateResources.reduce((prev: BN, coin: Resource) => { - request.addResource({ - ...coin, - predicate: predicate.bytes, - predicateData: encoded, - } as unknown as Resource); - request.outputs = []; - - return prev.add(coin.amount); - }, bn(0)); - - // output sent to receiver - request.addCoinOutput(receiverAddress, totalInPredicate, assetId); - - const requiredCoinQuantities: CoinQuantityLike[] = []; - if (options.fundTransaction) { - requiredCoinQuantities.push(request.calculateFee()); - } - - if (requiredCoinQuantities.length && walletAddress) { - const resources = await this.getResourcesToSpend(walletAddress, requiredCoinQuantities); - request.addResources(resources); - } - - return request; - } - - async submitSpendPredicate( - predicate: AbstractPredicate, - amountToSpend: BigNumberish, - receiverAddress: AbstractAddress, - predicateData?: InputValue[], - assetId: BytesLike = NativeAssetId, - options?: BuildPredicateOptions, - walletAddress?: AbstractAddress - ): Promise> { - const request = await this.buildSpendPredicate( - predicate, - amountToSpend, - receiverAddress, - predicateData, - assetId, - options, - walletAddress - ); - - try { - const response = await this.sendTransaction(request); - return await response.waitForResult(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (error: any) { - const errors: { message: string }[] = error?.response?.errors || []; - if ( - errors.some(({ message }) => - message.includes('unexpected block execution error TransactionValidity(InvalidPredicate') - ) - ) { - throw new Error('Invalid Predicate'); - } - - throw error; - } - } } diff --git a/packages/script/src/script.test.ts b/packages/script/src/script.test.ts index 11e9ab9423a..33cec184059 100644 --- a/packages/script/src/script.test.ts +++ b/packages/script/src/script.test.ts @@ -7,7 +7,7 @@ import { bn } from '@fuel-ts/math'; import type { CoinQuantityLike, TransactionResponse, TransactionResult } from '@fuel-ts/providers'; import { Provider, ScriptTransactionRequest } from '@fuel-ts/providers'; import { ReceiptType } from '@fuel-ts/transactions'; -import type { BaseWalletLocked } from '@fuel-ts/wallet'; +import type { Account } from '@fuel-ts/wallet'; import { generateTestWallet } from '@fuel-ts/wallet/test-utils'; import { readFileSync } from 'fs'; import { join } from 'path'; @@ -29,7 +29,7 @@ const setup = async () => { // #region typedoc:script-call const callScript = async ( - wallet: BaseWalletLocked, + account: Account, script: Script, data: TData ): Promise<{ @@ -49,11 +49,11 @@ const callScript = async ( // Get and add required coins to the transaction if (requiredCoinQuantities.length) { - const resources = await wallet.getResourcesToSpend(requiredCoinQuantities); + const resources = await account.getResourcesToSpend(requiredCoinQuantities); request.addResources(resources); } - const response = await wallet.sendTransaction(request); + const response = await account.sendTransaction(request); const transactionResult = await response.waitForResult(); const result = script.decodeCallResult(transactionResult); diff --git a/packages/wallet/src/wallet-locked.test.ts b/packages/wallet/src/account.test.ts similarity index 65% rename from packages/wallet/src/wallet-locked.test.ts rename to packages/wallet/src/account.test.ts index 79485a71681..d0455708ed5 100644 --- a/packages/wallet/src/wallet-locked.test.ts +++ b/packages/wallet/src/account.test.ts @@ -1,31 +1,28 @@ import { bn } from '@fuel-ts/math'; -import { Wallet } from './wallet'; -import type { WalletUnlocked } from './wallets'; +import { Account } from './account'; -describe('WalletLocked', () => { - let wallet: WalletUnlocked; +describe('Account', () => { const assets = [ '0x0101010101010101010101010101010101010101010101010101010101010101', '0x0202020202020202020202020202020202020202020202020202020202020202', '0x0000000000000000000000000000000000000000000000000000000000000000', ]; - beforeAll(() => { - wallet = Wallet.generate(); - }); - it('Create wallet using a address', async () => { - const walletLocked = Wallet.fromAddress(wallet.address); - - expect(walletLocked.address).toEqual(wallet.address); + const account = new Account( + '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db' + ); + expect(account.address.toB256()).toEqual( + '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db' + ); }); it('getCoins()', async () => { - const walletLocked = Wallet.fromAddress( + const account = new Account( '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db' ); - const coins = await walletLocked.getCoins(); + const coins = await account.getCoins(); const assetA = coins.find((c) => c.assetId === assets[0]); expect(assetA?.amount.gt(1)).toBeTruthy(); const assetB = coins.find((c) => c.assetId === assets[1]); @@ -36,10 +33,10 @@ describe('WalletLocked', () => { it('getResourcesToSpend()', async () => { // #region typedoc:Message-getResourcesToSpend - const walletLocked = Wallet.fromAddress( + const account = new Account( '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db' ); - const resourcesToSpend = await walletLocked.getResourcesToSpend([ + const resourcesToSpend = await account.getResourcesToSpend([ { amount: bn(2), assetId: '0x0101010101010101010101010101010101010101010101010101010101010101', @@ -50,28 +47,28 @@ describe('WalletLocked', () => { }); it('getMessages()', async () => { - const walletLocked = Wallet.fromAddress( + const account = new Account( '0x69a2b736b60159b43bb8a4f98c0589f6da5fa3a3d101e8e269c499eb942753ba' ); - const messages = await walletLocked.getMessages(); + const messages = await account.getMessages(); expect(messages.length).toEqual(1); }); it('getBalance()', async () => { - const walletLocked = Wallet.fromAddress( + const account = new Account( '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db' ); - const balanceA = await walletLocked.getBalance(assets[0]); - const balanceB = await walletLocked.getBalance(assets[1]); + const balanceA = await account.getBalance(assets[0]); + const balanceB = await account.getBalance(assets[1]); expect(balanceA.gte(1)).toBeTruthy(); expect(balanceB.gte(1)).toBeTruthy(); }); it('getBalances()', async () => { - const walletLocked = Wallet.fromAddress( + const account = new Account( '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db' ); - const balances = await walletLocked.getBalances(); + const balances = await account.getBalances(); expect(balances.length).toBeGreaterThanOrEqual(1); }); }); diff --git a/packages/wallet/src/base-locked-wallet.ts b/packages/wallet/src/account.ts similarity index 74% rename from packages/wallet/src/base-locked-wallet.ts rename to packages/wallet/src/account.ts index 3f60a98b1d8..b32029cf0f1 100644 --- a/packages/wallet/src/base-locked-wallet.ts +++ b/packages/wallet/src/account.ts @@ -1,10 +1,9 @@ import type { BytesLike } from '@ethersproject/bytes'; import { arrayify, hexlify } from '@ethersproject/bytes'; -import type { InputValue } from '@fuel-ts/abi-coder'; -import { Address, addressify } from '@fuel-ts/address'; +import { Address } from '@fuel-ts/address'; import { NativeAssetId } from '@fuel-ts/constants'; -import { AbstractWallet } from '@fuel-ts/interfaces'; -import type { AbstractAddress, AbstractPredicate } from '@fuel-ts/interfaces'; +import { AbstractAccount } from '@fuel-ts/interfaces'; +import type { AbstractAddress } from '@fuel-ts/interfaces'; import type { BigNumberish, BN } from '@fuel-ts/math'; import { bn } from '@fuel-ts/math'; import type { @@ -15,8 +14,6 @@ import type { Coin, CoinQuantityLike, CoinQuantity, - BuildPredicateOptions, - TransactionResult, Message, Resource, ExcludeResourcesOption, @@ -32,25 +29,17 @@ import { MAX_GAS_PER_TX } from '@fuel-ts/transactions'; import { FUEL_NETWORK_URL } from './constants'; /** - * BaseWallet + * Account */ -export class BaseWalletLocked extends AbstractWallet { - private readonly _address: AbstractAddress; +export class Account extends AbstractAccount { + readonly address: AbstractAddress; provider: Provider; - constructor(publicKey: string | AbstractAddress, provider: string | Provider = FUEL_NETWORK_URL) { + constructor(address: string | AbstractAddress, provider: string | Provider = FUEL_NETWORK_URL) { super(); this.provider = this.connect(provider); - if (typeof publicKey === 'string') { - this._address = Address.fromString(publicKey); - } else { - this._address = addressify(publicKey); - } - } - - get address(): AbstractAddress { - return this._address; + this.address = Address.fromDynamicInput(address); } /** @@ -281,68 +270,4 @@ export class BaseWalletLocked extends AbstractWallet { await this.provider.addMissingVariables(transactionRequest); return this.provider.simulate(transactionRequest); } - - async buildPredicateTransaction( - predicateAddress: AbstractAddress, - amountToPredicate: BigNumberish, - assetId: BytesLike = NativeAssetId, - predicateOptions?: BuildPredicateOptions - ): Promise { - const options = { - fundTransaction: true, - ...predicateOptions, - }; - const request = new ScriptTransactionRequest({ - gasLimit: MAX_GAS_PER_TX, - ...options, - }); - - // output is locked behind predicate - request.addCoinOutput(predicateAddress, amountToPredicate, assetId); - - const requiredCoinQuantities: CoinQuantityLike[] = []; - if (options.fundTransaction) { - requiredCoinQuantities.push(request.calculateFee()); - } - - if (requiredCoinQuantities.length) { - const resources = await this.getResourcesToSpend(requiredCoinQuantities); - request.addResources(resources); - } - - return request; - } - - async submitPredicate( - predicateAddress: AbstractAddress, - amountToPredicate: BigNumberish, - assetId: BytesLike = NativeAssetId, - options?: BuildPredicateOptions - ): Promise> { - const request = await this.buildPredicateTransaction( - predicateAddress, - amountToPredicate, - assetId, - options - ); - const response = await this.sendTransaction(request); - return response.waitForResult(); - } - - async submitSpendPredicate( - predicate: AbstractPredicate, - amountToSpend: BigNumberish, - predicateData?: InputValue[], - assetId: BytesLike = NativeAssetId, - options?: BuildPredicateOptions - ): Promise> { - return this.provider.submitSpendPredicate( - predicate, - amountToSpend, - this.address, - predicateData, - assetId, - options - ); - } } diff --git a/packages/wallet/src/base-unlocked-wallet.ts b/packages/wallet/src/base-unlocked-wallet.ts index 55f970797a3..e3081328337 100644 --- a/packages/wallet/src/base-unlocked-wallet.ts +++ b/packages/wallet/src/base-unlocked-wallet.ts @@ -9,13 +9,13 @@ import type { import { transactionRequestify } from '@fuel-ts/providers'; import { Signer } from '@fuel-ts/signer'; -import { BaseWalletLocked } from './base-locked-wallet'; +import { Account } from './account'; import { FUEL_NETWORK_URL } from './constants'; /** * BaseWalletUnlocked */ -export class BaseWalletUnlocked extends BaseWalletLocked { +export class BaseWalletUnlocked extends Account { /* default HDWallet path */ static defaultPath = "m/44'/1179993420'/0'/0/0"; diff --git a/packages/wallet/src/index.ts b/packages/wallet/src/index.ts index 435b0276c06..9e755c6bed0 100644 --- a/packages/wallet/src/index.ts +++ b/packages/wallet/src/index.ts @@ -1,4 +1,4 @@ -export * from './base-locked-wallet'; export * from './base-unlocked-wallet'; +export * from './account'; export * from './wallet'; export * from './wallets'; diff --git a/packages/wallet/src/transfer.test.ts b/packages/wallet/src/transfer.test.ts index 634daecbc58..285a11d7967 100644 --- a/packages/wallet/src/transfer.test.ts +++ b/packages/wallet/src/transfer.test.ts @@ -96,11 +96,11 @@ describe('Wallet', () => { ]); request.addResources(resources); - request.addCoinOutputs(receiverA, [ + request.addCoinOutputs(receiverA.address, [ [amount, assetIdA], [amount, assetIdB], ]); - request.addCoinOutputs(receiverB, [ + request.addCoinOutputs(receiverB.address, [ [amount, assetIdA], [amount, assetIdB], ]); diff --git a/packages/wallet/src/wallets.ts b/packages/wallet/src/wallets.ts index 01bf1519009..dc3de3dec36 100644 --- a/packages/wallet/src/wallets.ts +++ b/packages/wallet/src/wallets.ts @@ -9,14 +9,14 @@ import { Mnemonic } from '@fuel-ts/mnemonic'; import type { Provider } from '@fuel-ts/providers'; import { Signer } from '@fuel-ts/signer'; -import { BaseWalletLocked } from './base-locked-wallet'; +import { Account } from './account'; import { BaseWalletUnlocked } from './base-unlocked-wallet'; import type { GenerateOptions } from './types/GenerateOptions'; /** * WalletLocked */ -export class WalletLocked extends BaseWalletLocked { +export class WalletLocked extends Account { unlock(privateKey: BytesLike) { // eslint-disable-next-line @typescript-eslint/no-use-before-define return new WalletUnlocked(privateKey, this.provider); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4934aa12152..141ac8fe749 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -156,7 +156,6 @@ importers: '@ethersproject/bytes': ^5.7.0 '@ethersproject/logger': ^5.7.0 '@ethersproject/sha2': ^5.7.0 - '@fuel-ts/constants': workspace:* '@fuel-ts/interfaces': workspace:* '@fuel-ts/keystore': workspace:* '@fuel-ts/testcases': workspace:* @@ -166,7 +165,6 @@ importers: '@ethersproject/bytes': 5.7.0 '@ethersproject/logger': 5.7.0 '@ethersproject/sha2': 5.7.0 - '@fuel-ts/constants': link:../constants '@fuel-ts/interfaces': link:../interfaces '@fuel-ts/keystore': link:../keystore '@fuel-ts/versions': link:../versions @@ -414,17 +412,26 @@ importers: '@ethersproject/logger': ^5.7.0 '@fuel-ts/abi-coder': workspace:* '@fuel-ts/address': workspace:* - '@fuel-ts/contract': workspace:* '@fuel-ts/interfaces': workspace:* + '@fuel-ts/math': workspace:* + '@fuel-ts/merkle': workspace:* + '@fuel-ts/providers': workspace:* + '@fuel-ts/transactions': workspace:* '@fuel-ts/versions': workspace:* + '@fuel-ts/wallet': workspace:* dependencies: '@ethersproject/bytes': 5.7.0 '@ethersproject/logger': 5.7.0 '@fuel-ts/abi-coder': link:../abi-coder '@fuel-ts/address': link:../address - '@fuel-ts/contract': link:../contract '@fuel-ts/interfaces': link:../interfaces + '@fuel-ts/merkle': link:../merkle + '@fuel-ts/providers': link:../providers + '@fuel-ts/transactions': link:../transactions '@fuel-ts/versions': link:../versions + '@fuel-ts/wallet': link:../wallet + devDependencies: + '@fuel-ts/math': link:../math packages/providers: specifiers: diff --git a/services/fuel-core/Dockerfile b/services/fuel-core/Dockerfile index b75fc5fc548..974b79b1467 100644 --- a/services/fuel-core/Dockerfile +++ b/services/fuel-core/Dockerfile @@ -1,4 +1,4 @@ -FROM ghcr.io/fuellabs/fuel-core:v0.17.1 +FROM ghcr.io/fuellabs/fuel-core:v0.17.2 ARG IP=0.0.0.0 ARG PORT=4000 @@ -26,6 +26,7 @@ CMD exec ./fuel-core run \ --vm-backtrace \ --poa-instant=true \ --consensus-key ${CONSENSUS_KEY} \ + --utxo-validation \ --chain ./chainConfig.json EXPOSE ${PORT} diff --git a/typedoc.json b/typedoc.json index e99815d5a35..e8e6310a7b1 100644 --- a/typedoc.json +++ b/typedoc.json @@ -24,7 +24,8 @@ "packages/transactions", "packages/wallet", "packages/wallet-manager", - "packages/wordlists" + "packages/wordlists", + "packages/account" ], "entryPointStrategy": "packages", "githubPages": false,