diff --git a/.changeset/khaki-trees-wink.md b/.changeset/khaki-trees-wink.md new file mode 100644 index 00000000000..a423891100a --- /dev/null +++ b/.changeset/khaki-trees-wink.md @@ -0,0 +1,5 @@ +--- +"fuels": minor +--- + +feat!: `onDeploy` fuels config supports all Sway program types diff --git a/.changeset/little-moons-drive.md b/.changeset/little-moons-drive.md new file mode 100644 index 00000000000..0146c440977 --- /dev/null +++ b/.changeset/little-moons-drive.md @@ -0,0 +1,6 @@ +--- +"@fuel-ts/contract": minor +"@fuel-ts/account": patch +--- + +chore!: refactor predicate and script deployment diff --git a/.changeset/many-rings-joke.md b/.changeset/many-rings-joke.md new file mode 100644 index 00000000000..d087a211fa9 --- /dev/null +++ b/.changeset/many-rings-joke.md @@ -0,0 +1,5 @@ +--- +"@fuel-ts/account": minor +--- + +chore!: mandate `abi` in `Predicate` constructor diff --git a/.changeset/orange-panthers-lay.md b/.changeset/orange-panthers-lay.md new file mode 100644 index 00000000000..c87f6bfe52a --- /dev/null +++ b/.changeset/orange-panthers-lay.md @@ -0,0 +1,4 @@ +--- +--- + +chore: use typegen'd outputs in some fuel-gauge tests diff --git a/.changeset/smart-olives-attend.md b/.changeset/smart-olives-attend.md new file mode 100644 index 00000000000..1a850a4ec23 --- /dev/null +++ b/.changeset/smart-olives-attend.md @@ -0,0 +1,5 @@ +--- +"@fuel-ts/account": minor +--- + +chore!: remove `blockId` in transaction list responses diff --git a/.changeset/thirty-waves-grin.md b/.changeset/thirty-waves-grin.md new file mode 100644 index 00000000000..bd59a9a36d5 --- /dev/null +++ b/.changeset/thirty-waves-grin.md @@ -0,0 +1,5 @@ +--- +"fuels": patch +--- + +fix: bump proxy contract versions diff --git a/.changeset/yellow-trees-talk.md b/.changeset/yellow-trees-talk.md new file mode 100644 index 00000000000..8556e240630 --- /dev/null +++ b/.changeset/yellow-trees-talk.md @@ -0,0 +1,6 @@ +--- +"@fuel-ts/transactions": patch +"@fuel-ts/account": patch +--- + +chore: fix receipts properties and deprecate incorrect ones diff --git a/apps/demo-fuels/fuels.config.full.ts b/apps/demo-fuels/fuels.config.full.ts index 28b01d8831f..92c7cf944ed 100644 --- a/apps/demo-fuels/fuels.config.full.ts +++ b/apps/demo-fuels/fuels.config.full.ts @@ -1,6 +1,6 @@ /* eslint-disable no-console */ import { createConfig } from 'fuels'; -import type { ContractDeployOptions, DeployedContract, FuelsConfig } from 'fuels'; +import type { ContractDeployOptions, DeployedData, FuelsConfig } from 'fuels'; const MY_FIRST_DEPLOYED_CONTRACT_NAME = ''; @@ -91,9 +91,9 @@ export default createConfig({ // #endregion onBuild // #region onDeploy - // #import { DeployedContract, FuelsConfig }; + // #import { DeployedData, FuelsConfig }; - onDeploy: (config: FuelsConfig, data: DeployedContract[]) => { + onDeploy: (config: FuelsConfig, data: DeployedData) => { console.log('fuels:onDeploy', { config, data }); }, // #endregion onDeploy diff --git a/apps/docs-snippets2/src/predicates/deploying-predicates.ts b/apps/docs-snippets2/src/predicates/deploying-predicates.ts index 6df4e5b1a46..1be4a208ee2 100644 --- a/apps/docs-snippets2/src/predicates/deploying-predicates.ts +++ b/apps/docs-snippets2/src/predicates/deploying-predicates.ts @@ -1,5 +1,5 @@ // #region full -import { Provider, Wallet, ContractFactory } from 'fuels'; +import { Provider, Wallet } from 'fuels'; import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../env'; import { ConfigurablePin, ConfigurablePinLoader } from '../typegend/predicates'; @@ -10,20 +10,18 @@ const wallet = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider); const receiver = Wallet.generate({ provider }); const baseAssetId = provider.getBaseAssetId(); -// We can deploy dyanmically or via `fuels deploy` -const factory = new ContractFactory( - ConfigurablePin.bytecode, - ConfigurablePin.abi, - wallet -); -const { waitForResult: waitForDeploy } = - await factory.deployAsBlobTxForScript(); +// We can deploy dynamically or via `fuels deploy` +const originalPredicate = new ConfigurablePin({ + provider, +}); + +const { waitForResult: waitForDeploy } = await originalPredicate.deploy(wallet); await waitForDeploy(); // First, we will need to instantiate the script via it's loader bytecode. // This can be imported from the typegen outputs that were created on `fuels deploy`. // Then we can use the predicate as we would normally, such as overriding the configurables. -const predicate = new ConfigurablePinLoader({ +const loaderPredicate = new ConfigurablePinLoader({ data: [23], provider, configurableConstants: { @@ -32,11 +30,15 @@ const predicate = new ConfigurablePinLoader({ }); // Now, let's fund the predicate -const fundTx = await wallet.transfer(predicate.address, 100_000, baseAssetId); +const fundTx = await wallet.transfer( + loaderPredicate.address, + 100_000, + baseAssetId +); await fundTx.waitForResult(); // Then we'll execute the transfer and validate the predicate -const transferTx = await predicate.transfer( +const transferTx = await loaderPredicate.transfer( receiver.address, 1000, baseAssetId @@ -44,5 +46,5 @@ const transferTx = await predicate.transfer( const { isStatusSuccess } = await transferTx.waitForResult(); // #endregion full -console.log('Predicate defined', predicate); +console.log('Predicate defined', loaderPredicate); console.log('Should fund predicate successfully', isStatusSuccess); diff --git a/apps/docs-snippets2/src/scripts/deploying-scripts.ts b/apps/docs-snippets2/src/scripts/deploying-scripts.ts index 4e123965baa..51d30054277 100644 --- a/apps/docs-snippets2/src/scripts/deploying-scripts.ts +++ b/apps/docs-snippets2/src/scripts/deploying-scripts.ts @@ -1,4 +1,4 @@ -import { ContractFactory, Provider, Wallet, hexlify } from 'fuels'; +import { Provider, Wallet, hexlify } from 'fuels'; import { launchTestNode } from 'fuels/test-utils'; import { @@ -16,13 +16,9 @@ const { const providerUrl = testProvider.url; const WALLET_PVT_KEY = hexlify(testWallet.privateKey); -const factory = new ContractFactory( - TypegenScript.bytecode, - TypegenScript.abi, - testWallet -); +const originalScript = new TypegenScript(testWallet); const { waitForResult: waitForDeploy } = - await factory.deployAsBlobTxForScript(); + await originalScript.deploy(testWallet); await waitForDeploy(); // #region deploying-scripts diff --git a/packages/account/src/index.ts b/packages/account/src/index.ts index 7c470acebd4..49203ab33c3 100644 --- a/packages/account/src/index.ts +++ b/packages/account/src/index.ts @@ -8,3 +8,4 @@ export * from './wallet-manager'; export * from './predicate'; export * from './providers'; export * from './connectors'; +export { deployScriptOrPredicate } from './utils/deployScriptOrPredicate'; diff --git a/packages/account/src/predicate/predicate.ts b/packages/account/src/predicate/predicate.ts index 3282657f542..a8c0b80fb4a 100644 --- a/packages/account/src/predicate/predicate.ts +++ b/packages/account/src/predicate/predicate.ts @@ -3,7 +3,7 @@ import { Interface } from '@fuel-ts/abi-coder'; import { Address } from '@fuel-ts/address'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; import type { BytesLike } from '@fuel-ts/interfaces'; -import { arrayify, hexlify, concat } from '@fuel-ts/utils'; +import { arrayify, hexlify } from '@fuel-ts/utils'; import type { FakeResources } from '../account'; import { Account } from '../account'; @@ -23,6 +23,7 @@ import type { TransactionRequestLike, TransactionResponse, } from '../providers'; +import { deployScriptOrPredicate } from '../utils/deployScriptOrPredicate'; import { getPredicateRoot } from './utils'; @@ -32,19 +33,11 @@ export type PredicateParams< > = { bytecode: BytesLike; provider: Provider; - abi?: JsonAbi; + abi: JsonAbi; data?: TData; configurableConstants?: TConfigurables; - loaderBytecode?: BytesLike; }; -function getDataOffset(binary: Uint8Array): number { - const buffer = binary.buffer.slice(binary.byteOffset + 8, binary.byteOffset + 16); - const dataView = new DataView(buffer); - const dataOffset = dataView.getBigUint64(0, false); // big-endian - return Number(dataOffset); -} - /** * `Predicate` provides methods to populate transaction data with predicate information and sending transactions with them. */ @@ -54,8 +47,7 @@ export class Predicate< > extends Account { bytes: Uint8Array; predicateData: TData = [] as unknown as TData; - interface?: Interface; - loaderBytecode: BytesLike = ''; + interface: Interface; /** * Creates an instance of the Predicate class. @@ -72,12 +64,6 @@ export class Predicate< provider, data, configurableConstants, - /** - * TODO: Implement a getBytes method within the Predicate class. This method should return the loaderBytecode if it is set. - * The getBytes method should be used in all places where we use this.bytes. - * Note: Do not set loaderBytecode to a default string here; it should remain undefined when not provided. - */ - loaderBytecode = '', }: PredicateParams) { const { predicateBytes, predicateInterface } = Predicate.processPredicateData( bytecode, @@ -89,7 +75,6 @@ export class Predicate< this.bytes = predicateBytes; this.interface = predicateInterface; - this.loaderBytecode = loaderBytecode; if (data !== undefined && data.length > 0) { this.predicateData = data; } @@ -168,20 +153,17 @@ export class Predicate< */ private static processPredicateData( bytes: BytesLike, - jsonAbi?: JsonAbi, + jsonAbi: JsonAbi, configurableConstants?: { [name: string]: unknown } ) { let predicateBytes = arrayify(bytes); - let abiInterface: Interface | undefined; + const abiInterface: Interface = new Interface(jsonAbi); - if (jsonAbi) { - abiInterface = new Interface(jsonAbi); - if (abiInterface.functions.main === undefined) { - throw new FuelError( - ErrorCode.ABI_MAIN_METHOD_MISSING, - 'Cannot use ABI without "main" function.' - ); - } + if (abiInterface.functions.main === undefined) { + throw new FuelError( + ErrorCode.ABI_MAIN_METHOD_MISSING, + 'Cannot use ABI without "main" function.' + ); } if (configurableConstants && Object.keys(configurableConstants).length) { @@ -246,19 +228,11 @@ export class Predicate< private static setConfigurableConstants( bytes: Uint8Array, configurableConstants: { [name: string]: unknown }, - abiInterface?: Interface, - loaderBytecode?: BytesLike + abiInterface: Interface ) { const mutatedBytes = bytes; try { - if (!abiInterface) { - throw new FuelError( - ErrorCode.INVALID_CONFIGURABLE_CONSTANTS, - 'Cannot validate configurable constants because the Predicate was instantiated without a JSON ABI' - ); - } - if (Object.keys(abiInterface.configurables).length === 0) { throw new FuelError( ErrorCode.INVALID_CONFIGURABLE_CONSTANTS, @@ -280,26 +254,6 @@ export class Predicate< mutatedBytes.set(encoded, offset); }); - - if (loaderBytecode) { - /** - * TODO: We mutate the predicate bytes here to be the loader bytes only if the configurables are being set. - * What we actually need to do here is to mutate the loader bytes to include the configurables. - */ - const offset = getDataOffset(bytes); - - // update the dataSection here as necessary (with configurables) - const dataSection = mutatedBytes.slice(offset); - - const dataSectionLen = dataSection.length; - - // Convert dataSectionLen to big-endian bytes - const dataSectionLenBytes = new Uint8Array(8); - const dataSectionLenDataView = new DataView(dataSectionLenBytes.buffer); - dataSectionLenDataView.setBigUint64(0, BigInt(dataSectionLen), false); - - mutatedBytes.set(concat([loaderBytecode, dataSectionLenBytes, dataSection])); - } } catch (err) { throw new FuelError( ErrorCode.INVALID_CONFIGURABLE_CONSTANTS, @@ -348,4 +302,28 @@ export class Predicate< return index; } + + /** + * + * @param account - The account used to pay the deployment costs. + * @returns The _blobId_ and a _waitForResult_ callback that returns the deployed predicate + * once the blob deployment transaction finishes. + * + * The returned loader predicate will have the same configurable constants + * as the original predicate which was used to generate the loader predicate. + */ + async deploy(account: Account) { + return deployScriptOrPredicate({ + deployer: account, + abi: this.interface.jsonAbi, + bytecode: this.bytes, + loaderInstanceCallback: (loaderBytecode, newAbi) => + new Predicate({ + bytecode: loaderBytecode, + abi: newAbi, + provider: this.provider, + data: this.predicateData, + }) as T, + }); + } } diff --git a/packages/account/src/providers/operations.graphql b/packages/account/src/providers/operations.graphql index 3dd5c17dece..97f5bb2916c 100644 --- a/packages/account/src/providers/operations.graphql +++ b/packages/account/src/providers/operations.graphql @@ -12,9 +12,6 @@ fragment SubmittedStatusFragment on SubmittedStatus { fragment SuccessStatusFragment on SuccessStatus { type: __typename - block { - id - } time programState { returnType @@ -27,11 +24,15 @@ fragment SuccessStatusFragment on SuccessStatus { totalFee } -fragment FailureStatusFragment on FailureStatus { - type: __typename +fragment SuccessStatusWithBlockIdFragment on SuccessStatus { + ...SuccessStatusFragment block { id } +} + +fragment FailureStatusFragment on FailureStatus { + type: __typename totalGas totalFee time @@ -41,6 +42,13 @@ fragment FailureStatusFragment on FailureStatus { } } +fragment FailureStatusWithBlockIdFragment on FailureStatus { + ...FailureStatusFragment + block { + id + } +} + fragment SqueezedOutStatusFragment on SqueezedOutStatus { type: __typename reason @@ -106,13 +114,13 @@ fragment transactionStatusSubscriptionFragment on TransactionStatus { ...SubmittedStatusFragment } ... on SuccessStatus { - ...SuccessStatusFragment + ...SuccessStatusWithBlockIdFragment transaction { ...malleableTransactionFieldsFragment } } ... on FailureStatus { - ...FailureStatusFragment + ...FailureStatusWithBlockIdFragment transaction { ...malleableTransactionFieldsFragment } @@ -481,7 +489,22 @@ query getTransaction($transactionId: TransactionId!) { query getTransactionWithReceipts($transactionId: TransactionId!) { transaction(id: $transactionId) { - ...transactionFragment + id + rawPayload + status { + ... on SubmittedStatus { + ...SubmittedStatusFragment + } + ... on SuccessStatus { + ...SuccessStatusWithBlockIdFragment + } + ... on FailureStatus { + ...FailureStatusWithBlockIdFragment + } + ... on SqueezedOutStatus { + ...SqueezedOutStatusFragment + } + } } } diff --git a/packages/account/src/providers/provider.test.ts b/packages/account/src/providers/provider.test.ts index f8791c58505..226c6642e00 100644 --- a/packages/account/src/providers/provider.test.ts +++ b/packages/account/src/providers/provider.test.ts @@ -365,6 +365,10 @@ describe('Provider', () => { val1: bn(186), val2: bn(0), val3: bn(0), + ra: bn(202), + rb: bn(186), + rc: bn(0), + rd: bn(0), pc: bn(0x2888), is: bn(0x2880), }, diff --git a/packages/account/src/providers/transaction-request/blob-transaction-request.ts b/packages/account/src/providers/transaction-request/blob-transaction-request.ts index 1eb53255796..db90b4f16d4 100644 --- a/packages/account/src/providers/transaction-request/blob-transaction-request.ts +++ b/packages/account/src/providers/transaction-request/blob-transaction-request.ts @@ -12,7 +12,7 @@ import { BaseTransactionRequest, TransactionType } from './transaction-request'; export interface BlobTransactionRequestLike extends BaseTransactionRequestLike { /** Blob ID */ blobId: string; - /** Witness index of contract bytecode to create */ + /** Witness index of the bytecode to create */ witnessIndex?: number; } @@ -25,7 +25,7 @@ export class BlobTransactionRequest extends BaseTransactionRequest { type = TransactionType.Blob as const; /** Blob ID */ blobId: string; - /** Witness index of contract bytecode to create */ + /** Witness index of the bytecode to create */ witnessIndex: number; /** diff --git a/packages/account/src/providers/transaction-summary/assemble-transaction-summary.test.ts b/packages/account/src/providers/transaction-summary/assemble-transaction-summary.test.ts index 5bf84d661b0..f8cf0ff777b 100644 --- a/packages/account/src/providers/transaction-summary/assemble-transaction-summary.test.ts +++ b/packages/account/src/providers/transaction-summary/assemble-transaction-summary.test.ts @@ -86,7 +86,7 @@ describe('TransactionSummary', () => { const expected = { id, - blockId: MOCK_SUCCESS_STATUS.block.id, + blockId: MOCK_SUCCESS_STATUS.block?.id, fee: expect.any(BN), gasUsed: expect.any(BN), isTypeCreate: false, @@ -113,7 +113,7 @@ describe('TransactionSummary', () => { const expected = { id, - blockId: MOCK_FAILURE_STATUS.block.id, + blockId: MOCK_FAILURE_STATUS.block?.id, fee: expect.any(BN), gasUsed: expect.any(BN), isTypeCreate: false, diff --git a/packages/account/src/providers/transaction-summary/operations.test.ts b/packages/account/src/providers/transaction-summary/operations.test.ts index 9a00406afc2..b6d7cae9b70 100644 --- a/packages/account/src/providers/transaction-summary/operations.test.ts +++ b/packages/account/src/providers/transaction-summary/operations.test.ts @@ -882,6 +882,7 @@ describe('operations', () => { amount: bn('0x5f5e100'), assetId: '0x0000000000000000000000000000000000000000000000000000000000000000', from: '0x0a98320d39c03337401a4e46263972a9af6ce69ec2f35a5420b1bd35784c74b1', + id: '0x0a98320d39c03337401a4e46263972a9af6ce69ec2f35a5420b1bd35784c74b1', is: bn('0x4370'), pc: bn('0x57dc'), to: '0x3e7ddda4d0d3f8307ae5f1aed87623992c1c4decefec684936960775181b2302', diff --git a/packages/account/src/providers/transaction-summary/status.ts b/packages/account/src/providers/transaction-summary/status.ts index aa5c6ced2b1..d919c6efb50 100644 --- a/packages/account/src/providers/transaction-summary/status.ts +++ b/packages/account/src/providers/transaction-summary/status.ts @@ -52,7 +52,7 @@ export const processGraphqlStatus = (gqlTransactionStatus?: GraphqlTransactionSt switch (gqlTransactionStatus.type) { case 'SuccessStatus': time = gqlTransactionStatus.time; - blockId = gqlTransactionStatus.block.id; + blockId = gqlTransactionStatus.block?.id; isStatusSuccess = true; totalFee = bn(gqlTransactionStatus.totalFee); totalGas = bn(gqlTransactionStatus.totalGas); @@ -60,7 +60,7 @@ export const processGraphqlStatus = (gqlTransactionStatus?: GraphqlTransactionSt case 'FailureStatus': time = gqlTransactionStatus.time; - blockId = gqlTransactionStatus.block.id; + blockId = gqlTransactionStatus.block?.id; isStatusFailure = true; totalFee = bn(gqlTransactionStatus.totalFee); totalGas = bn(gqlTransactionStatus.totalGas); diff --git a/packages/account/src/providers/transaction-summary/types.ts b/packages/account/src/providers/transaction-summary/types.ts index 5ce748bffeb..e8913be7e9a 100644 --- a/packages/account/src/providers/transaction-summary/types.ts +++ b/packages/account/src/providers/transaction-summary/types.ts @@ -3,28 +3,60 @@ import type { B256Address } from '@fuel-ts/interfaces'; import type { BN, BNInput } from '@fuel-ts/math'; import type { Input, Output, Transaction, TransactionType } from '@fuel-ts/transactions'; -import type { - GqlFailureStatusFragment, - GqlGetTransactionQuery, - GqlSqueezedOutStatusFragment, - GqlSubmittedStatusFragment, - GqlSuccessStatusFragment, -} from '../__generated__/operations'; +import type { GqlReceiptFragment, GqlSuccessStatusFragment } from '../__generated__/operations'; import type { TransactionResultReceipt } from '../transaction-response'; -export type GqlTransaction = NonNullable; +export type SubmittedStatus = { + type: 'SubmittedStatus'; + time: string; +}; -export type GraphqlTransactionStatus = GqlTransaction['status']; +export type SuccessStatus = { + type: 'SuccessStatus'; + time: string; + programState?: GqlSuccessStatusFragment['programState']; + block?: { + id: string; + }; + receipts: GqlReceiptFragment[]; + totalGas: string; + totalFee: string; +}; -export type SuccessStatus = GqlSuccessStatusFragment; -export type FailureStatus = GqlFailureStatusFragment; -export type SubmittedStatus = GqlSubmittedStatusFragment; -export type SqueezedOutStatus = GqlSqueezedOutStatusFragment; +export type FailureStatus = { + type: 'FailureStatus'; + time: string; + reason: string; + block?: { + id: string; + }; + receipts: GqlReceiptFragment[]; + totalGas: string; + totalFee: string; +}; + +export type SqueezedOutStatus = { + type: 'SqueezedOutStatus'; + reason: string; +}; + +export type GraphqlTransactionStatus = + | SubmittedStatus + | SuccessStatus + | FailureStatus + | SqueezedOutStatus + | null; + +export type GqlTransaction = { + id: string; + rawPayload: string; + status?: GraphqlTransactionStatus; +}; export type Reason = FailureStatus['reason']; export type ProgramState = SuccessStatus['programState']; export type Time = SubmittedStatus['time'] | SuccessStatus['time'] | FailureStatus['time']; -export type BlockId = SuccessStatus['block']['id'] | FailureStatus['block']['id']; +export type BlockId = string; /** * @hidden diff --git a/packages/account/src/providers/utils/receipts.ts b/packages/account/src/providers/utils/receipts.ts index bfc1ec9b85f..f2cd9deacf7 100644 --- a/packages/account/src/providers/utils/receipts.ts +++ b/packages/account/src/providers/utils/receipts.ts @@ -66,9 +66,11 @@ export function assembleReceiptByType(receipt: GqlReceiptFragment) { switch (receiptType) { case GqlReceiptType.Call: { + const id = hexOrZero(receipt.id || receipt.contractId); const callReceipt: ReceiptCall = { type: ReceiptType.Call, - from: hexOrZero(receipt.id || receipt.contractId), + id, + from: id, to: hexOrZero(receipt?.to), amount: bn(receipt.amount), assetId: hexOrZero(receipt.assetId), @@ -134,13 +136,22 @@ export function assembleReceiptByType(receipt: GqlReceiptFragment) { } case GqlReceiptType.Log: { + const ra = bn(receipt.ra); + const rb = bn(receipt.rb); + const rc = bn(receipt.rc); + const rd = bn(receipt.rd); + const logReceipt: ReceiptLog = { type: ReceiptType.Log, id: hexOrZero(receipt.id || receipt.contractId), - val0: bn(receipt.ra), - val1: bn(receipt.rb), - val2: bn(receipt.rc), - val3: bn(receipt.rd), + ra, + rb, + rc, + rd, + val0: ra, + val1: rb, + val2: rc, + val3: rd, pc: bn(receipt.pc), is: bn(receipt.is), }; @@ -149,11 +160,15 @@ export function assembleReceiptByType(receipt: GqlReceiptFragment) { } case GqlReceiptType.LogData: { + const ra = bn(receipt.ra); + const rb = bn(receipt.rb); const logDataReceipt: ReceiptLogData = { type: ReceiptType.LogData, id: hexOrZero(receipt.id || receipt.contractId), - val0: bn(receipt.ra), - val1: bn(receipt.rb), + ra, + rb, + val0: ra, + val1: rb, ptr: bn(receipt.ptr), len: bn(receipt.len), digest: hexOrZero(receipt.digest), @@ -165,9 +180,11 @@ export function assembleReceiptByType(receipt: GqlReceiptFragment) { } case GqlReceiptType.Transfer: { + const id = hexOrZero(receipt.id || receipt.contractId); const transferReceipt: ReceiptTransfer = { type: ReceiptType.Transfer, - from: hexOrZero(receipt.id || receipt.contractId), + id, + from: id, to: hexOrZero(receipt.toAddress || receipt?.to), amount: bn(receipt.amount), assetId: hexOrZero(receipt.assetId), @@ -179,9 +196,11 @@ export function assembleReceiptByType(receipt: GqlReceiptFragment) { } case GqlReceiptType.TransferOut: { + const id = hexOrZero(receipt.id || receipt.contractId); const transferOutReceipt: ReceiptTransferOut = { type: ReceiptType.TransferOut, - from: hexOrZero(receipt.id || receipt.contractId), + id, + from: id, to: hexOrZero(receipt.toAddress || receipt.to), amount: bn(receipt.amount), assetId: hexOrZero(receipt.assetId), diff --git a/packages/account/src/utils/deployScriptOrPredicate.ts b/packages/account/src/utils/deployScriptOrPredicate.ts new file mode 100644 index 00000000000..3ef7dad9c70 --- /dev/null +++ b/packages/account/src/utils/deployScriptOrPredicate.ts @@ -0,0 +1,118 @@ +import type { JsonAbi } from '@fuel-ts/abi-coder'; +import { FuelError, ErrorCode } from '@fuel-ts/errors'; +import { hash } from '@fuel-ts/hasher'; +import { bn } from '@fuel-ts/math'; +import { arrayify } from '@fuel-ts/utils'; + +import type { Account } from '../account'; +import { BlobTransactionRequest, calculateGasFee, TransactionStatus } from '../providers'; + +import { + getDataOffset, + getPredicateScriptLoaderInstructions, +} from './predicate-script-loader-instructions'; + +async function fundBlobTx(deployer: Account, blobTxRequest: BlobTransactionRequest) { + // Check the account can afford to deploy all chunks and loader + let totalCost = bn(0); + const chainInfo = deployer.provider.getChain(); + const gasPrice = await deployer.provider.estimateGasPrice(10); + const priceFactor = chainInfo.consensusParameters.feeParameters.gasPriceFactor; + + const minGas = blobTxRequest.calculateMinGas(chainInfo); + + const minFee = calculateGasFee({ + gasPrice, + gas: minGas, + priceFactor, + tip: blobTxRequest.tip, + }).add(1); + + totalCost = totalCost.add(minFee); + + if (totalCost.gt(await deployer.getBalance())) { + throw new FuelError(ErrorCode.FUNDS_TOO_LOW, 'Insufficient balance to deploy predicate.'); + } + + const txCost = await deployer.getTransactionCost(blobTxRequest); + // eslint-disable-next-line no-param-reassign + blobTxRequest.maxFee = txCost.maxFee; + return deployer.fund(blobTxRequest, txCost); +} + +function adjustConfigurableOffsets(jsonAbi: JsonAbi, configurableOffsetDiff: number) { + const { configurables: readOnlyConfigurables } = jsonAbi; + const configurables: JsonAbi['configurables'] = []; + readOnlyConfigurables.forEach((config) => { + // @ts-expect-error shut up the read-only thing + configurables.push({ ...config, offset: config.offset - configurableOffsetDiff }); + }); + return { ...jsonAbi, configurables } as JsonAbi; +} + +interface Deployer { + deployer: Account; + bytecode: Uint8Array; + abi: JsonAbi; + loaderInstanceCallback: (loaderBytecode: Uint8Array, newAbi: JsonAbi) => T; +} + +export async function deployScriptOrPredicate({ + deployer, + bytecode, + abi, + loaderInstanceCallback, +}: Deployer) { + const dataSectionOffset = getDataOffset(arrayify(bytecode)); + const byteCodeWithoutDataSection = bytecode.slice(0, dataSectionOffset); + + // Generate the associated create tx for the loader contract + const blobId = hash(byteCodeWithoutDataSection); + + const blobTxRequest = new BlobTransactionRequest({ + blobId, + witnessIndex: 0, + witnesses: [byteCodeWithoutDataSection], + }); + + const { loaderBytecode, blobOffset } = getPredicateScriptLoaderInstructions( + arrayify(bytecode), + arrayify(blobId) + ); + + const configurableOffsetDiff = byteCodeWithoutDataSection.length - (blobOffset || 0); + const newAbi = adjustConfigurableOffsets(abi, configurableOffsetDiff); + + const blobExists = (await deployer.provider.getBlobs([blobId])).length > 0; + + const loaderInstance = loaderInstanceCallback(loaderBytecode, newAbi); + if (blobExists) { + return { + waitForResult: () => Promise.resolve(loaderInstance), + blobId, + }; + } + + const fundedBlobRequest = await fundBlobTx(deployer, blobTxRequest); + + // Transaction id is unset until we have funded the create tx, which is dependent on the blob tx + const waitForResult = async () => { + try { + const blobTx = await deployer.sendTransaction(fundedBlobRequest); + const result = await blobTx.waitForResult(); + + if (result.status !== TransactionStatus.success) { + throw new Error(); + } + } catch (err: unknown) { + throw new FuelError(ErrorCode.TRANSACTION_FAILED, 'Failed to deploy predicate chunk'); + } + + return loaderInstance; + }; + + return { + waitForResult, + blobId, + }; +} diff --git a/packages/contract/src/loader/predicate-script-loader-instructions.ts b/packages/account/src/utils/predicate-script-loader-instructions.ts similarity index 100% rename from packages/contract/src/loader/predicate-script-loader-instructions.ts rename to packages/account/src/utils/predicate-script-loader-instructions.ts diff --git a/packages/account/test/fixtures/transaction-summary.ts b/packages/account/test/fixtures/transaction-summary.ts index 10054eef528..9ed7e2bd689 100644 --- a/packages/account/test/fixtures/transaction-summary.ts +++ b/packages/account/test/fixtures/transaction-summary.ts @@ -109,6 +109,7 @@ export const MOCK_OUTPUT_CONTRACT_CREATED: OutputContractCreated = { export const MOCK_RECEIPT_CALL: TransactionResultCallReceipt = { type: ReceiptType.Call, from: '0x0000000000000000000000000000000000000000000000000000000000000000', + id: '0x0000000000000000000000000000000000000000000000000000000000000000', to: '0x0a98320d39c03337401a4e46263972a9af6ce69ec2f35a5420b1bd35784c74b1', amount: bn(100000000), assetId: '0x0000000000000000000000000000000000000000000000000000000000000000', @@ -129,6 +130,7 @@ export const MOCK_RECEIPT_RETURN: TransactionResultReturnReceipt = { export const MOCK_RECEIPT_TRANSFER: TransactionResultTransferReceipt = { type: ReceiptType.Transfer, + id: '0x0000000000000000000000000000000000000000000000000000000000000000', from: '0x0000000000000000000000000000000000000000000000000000000000000000', to: '0xaab4884920fa4d3a35fc2977cc442b0caddf87e001ef62321b6c02f5ab0f4115', amount: bn(988), @@ -139,6 +141,7 @@ export const MOCK_RECEIPT_TRANSFER: TransactionResultTransferReceipt = { export const MOCK_RECEIPT_TRANSFER_OUT: TransactionResultTransferOutReceipt = { type: ReceiptType.TransferOut, + id: '0x0a98320d39c03337401a4e46263972a9af6ce69ec2f35a5420b1bd35784c74b1', from: '0x0a98320d39c03337401a4e46263972a9af6ce69ec2f35a5420b1bd35784c74b1', to: '0x3e7ddda4d0d3f8307ae5f1aed87623992c1c4decefec684936960775181b2302', amount: bn(100000000), diff --git a/packages/account/test/predicate-functions.test.ts b/packages/account/test/predicate-functions.test.ts index cf6d5dec061..88d385e02de 100644 --- a/packages/account/test/predicate-functions.test.ts +++ b/packages/account/test/predicate-functions.test.ts @@ -18,6 +18,7 @@ describe('Predicate', () => { const predicate = new Predicate({ bytecode: predicateBytecode, + abi: predicateAbi, provider, }); expect(predicate.address.toB256()).toEqual(predicateAddress); diff --git a/packages/contract/src/contract-factory.ts b/packages/contract/src/contract-factory.ts index c79d338c7bb..9adcfb3a55b 100644 --- a/packages/contract/src/contract-factory.ts +++ b/packages/contract/src/contract-factory.ts @@ -21,14 +21,9 @@ import type { BytesLike } from '@fuel-ts/interfaces'; import { bn } from '@fuel-ts/math'; import { Contract } from '@fuel-ts/program'; import type { StorageSlot } from '@fuel-ts/transactions'; -import { arrayify, isDefined, hexlify } from '@fuel-ts/utils'; +import { arrayify, isDefined } from '@fuel-ts/utils'; -import { - getLoaderInstructions, - getPredicateScriptLoaderInstructions, - getContractChunks, - getDataOffset, -} from './loader'; +import { getLoaderInstructions, getContractChunks } from './loader'; import { getContractId, getContractStorageRoot, hexlifyWithPrefix } from './util'; /** Amount of percentage override for chunk sizes in blob transactions */ @@ -377,88 +372,6 @@ export default class ContractFactory { return { waitForResult, contractId, waitForTransactionId }; } - async deployAsBlobTxForScript(): Promise<{ - waitForResult: () => Promise<{ - loaderBytecode: string; - configurableOffsetDiff: number; - }>; - blobId: string; - }> { - const account = this.getAccount(); - - const dataSectionOffset = getDataOffset(arrayify(this.bytecode)); - const byteCodeWithoutDataSection = this.bytecode.slice(0, dataSectionOffset); - - // Generate the associated create tx for the loader contract - const blobId = hash(byteCodeWithoutDataSection); - - const bloTransactionRequest = this.blobTransactionRequest({ - bytecode: byteCodeWithoutDataSection, - }); - - const { loaderBytecode, blobOffset } = getPredicateScriptLoaderInstructions( - arrayify(this.bytecode), - arrayify(blobId) - ); - - const configurableOffsetDiff = byteCodeWithoutDataSection.length - (blobOffset || 0); - - const blobExists = (await account.provider.getBlobs([blobId])).length > 0; - if (blobExists) { - return { - waitForResult: () => - Promise.resolve({ loaderBytecode: hexlify(loaderBytecode), configurableOffsetDiff }), - blobId, - }; - } - - // Check the account can afford to deploy all chunks and loader - let totalCost = bn(0); - const chainInfo = account.provider.getChain(); - const gasPrice = await account.provider.estimateGasPrice(10); - const priceFactor = chainInfo.consensusParameters.feeParameters.gasPriceFactor; - - const minGas = bloTransactionRequest.calculateMinGas(chainInfo); - const minFee = calculateGasFee({ - gasPrice, - gas: minGas, - priceFactor, - tip: bloTransactionRequest.tip, - }).add(1); - - totalCost = totalCost.add(minFee); - - if (totalCost.gt(await account.getBalance())) { - throw new FuelError(ErrorCode.FUNDS_TOO_LOW, 'Insufficient balance to deploy contract.'); - } - - // Transaction id is unset until we have funded the create tx, which is dependent on the blob txs - const waitForResult = async () => { - // Deploy the chunks as blob txs - const fundedBlobRequest = await this.fundTransactionRequest(bloTransactionRequest); - - let result: TransactionResult; - - try { - const blobTx = await account.sendTransaction(fundedBlobRequest); - result = await blobTx.waitForResult(); - } catch (err: unknown) { - throw new FuelError(ErrorCode.TRANSACTION_FAILED, 'Failed to deploy contract chunk'); - } - - if (!result.status || result.status !== TransactionStatus.success) { - throw new FuelError(ErrorCode.TRANSACTION_FAILED, 'Failed to deploy contract chunk'); - } - - return { loaderBytecode: hexlify(loaderBytecode), configurableOffsetDiff }; - }; - - return { - waitForResult, - blobId, - }; - } - /** * Set configurable constants of the contract with the specified values. * diff --git a/packages/contract/src/loader/index.ts b/packages/contract/src/loader/index.ts index 5c888356c61..6dee5f1765d 100644 --- a/packages/contract/src/loader/index.ts +++ b/packages/contract/src/loader/index.ts @@ -1,4 +1,3 @@ export * from './loader-script'; -export * from './predicate-script-loader-instructions'; export * from './types'; export * from './utils'; diff --git a/packages/fuel-gauge/src/advanced-logging.test.ts b/packages/fuel-gauge/src/advanced-logging.test.ts index ad7f42a0616..44d6b51d469 100644 --- a/packages/fuel-gauge/src/advanced-logging.test.ts +++ b/packages/fuel-gauge/src/advanced-logging.test.ts @@ -1,5 +1,5 @@ import type { FuelError } from '@fuel-ts/errors'; -import { Script, bn } from 'fuels'; +import { bn } from 'fuels'; import { launchTestNode } from 'fuels/test-utils'; import { @@ -280,7 +280,7 @@ describe('Advanced Logging', () => { wallets: [wallet], } = launched; - const script = new Script(ScriptCallContract.bytecode, ScriptCallContract.abi, wallet); + const script = new ScriptCallContract(wallet); const { waitForResult } = await script.functions .main(advancedLogContract.id.toB256(), otherAdvancedLogContract.id.toB256(), amount) @@ -305,7 +305,7 @@ describe('Advanced Logging', () => { wallets: [wallet], } = launched; - const script = new Script(ScriptCallContract.bytecode, ScriptCallContract.abi, wallet); + const script = new ScriptCallContract(wallet); const request = await script.functions .main(advancedLogContract.id.toB256(), otherAdvancedLogContract.id.toB256(), amount) diff --git a/packages/fuel-gauge/src/blob-deploy.test.ts b/packages/fuel-gauge/src/blob-deploy.test.ts index d9160eb5312..9bbcd328d66 100644 --- a/packages/fuel-gauge/src/blob-deploy.test.ts +++ b/packages/fuel-gauge/src/blob-deploy.test.ts @@ -1,190 +1,114 @@ -import type { JsonAbi } from 'fuels'; -import { bn, ContractFactory, getRandomB256, hexlify, Predicate, Script, Wallet } from 'fuels'; +import type { Script } from 'fuels'; +import { getRandomB256, hexlify, Predicate, Wallet } from 'fuels'; import { launchTestNode } from 'fuels/test-utils'; import { - ScriptDummy, - PredicateFalseConfigurable, ScriptMainArgBool, PredicateTrue, - PredicateWithMoreConfigurables, ScriptWithMoreConfigurable, + PredicateWithConfigurable, + PredicateWithMoreConfigurables, + ScriptWithConfigurable, } from '../test/typegen'; /** * @group node + * @group browser */ -describe('first try', () => { - const mapToLoaderAbi = (jsonAbi: JsonAbi, configurableOffsetDiff: number) => { - const { configurables: readOnlyConfigurables } = jsonAbi; - const configurables: JsonAbi['configurables'] = []; - readOnlyConfigurables.forEach((config) => { - // @ts-expect-error shut up the read-only thing - configurables.push({ ...config, offset: config.offset - configurableOffsetDiff }); +describe('deploying blobs', () => { + function decodeConfigurables( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + program: Predicate | Script + ): Record { + const configurables: Record = {}; + + Object.entries(program.interface.configurables).forEach(([key, { offset, concreteTypeId }]) => { + const data = program.bytes.slice(offset); + configurables[key] = program.interface.decodeType(concreteTypeId, data)[0]; }); - return { ...jsonAbi, configurables } as JsonAbi; - }; - - it('should ensure deploy the same blob again will not throw error', async () => { - using launch = await launchTestNode(); - - const { - wallets: [wallet], - } = launch; - const spy = vi.spyOn(wallet.provider, 'sendTransaction'); + return configurables; + } - const factory = new ContractFactory(ScriptDummy.bytecode, ScriptDummy.abi, wallet); - const { waitForResult } = await factory.deployAsBlobTxForScript(); - const { loaderBytecode } = await waitForResult(); - - const { waitForResult: waitForResult2 } = await factory.deployAsBlobTxForScript(); - const { loaderBytecode: loaderBytecode2 } = await waitForResult2(); - - // Should deploy not deploy the same blob again - expect(spy).toHaveBeenCalledTimes(1); - expect(loaderBytecode).equals(loaderBytecode2); - - vi.restoreAllMocks(); - }); - - it('should deploy blob for a script transaction and submit it', async () => { + it('script blob deployment works (NO CONFIGURABLE)', async () => { using launch = await launchTestNode(); const { + provider, wallets: [wallet], } = launch; - const factory = new ContractFactory(ScriptDummy.bytecode, ScriptDummy.abi, wallet); - const { waitForResult } = await factory.deployAsBlobTxForScript(); + const script = new ScriptMainArgBool(wallet); - const { loaderBytecode } = await waitForResult(); + const { waitForResult, blobId } = await script.deploy(wallet); - expect(loaderBytecode).to.not.equal(hexlify(ScriptDummy.bytecode)); - }); + const loaderScript = await waitForResult(); - it('should deploy blob for a script transaction and submit it (NO CONFIGURABLE)', async () => { - using launch = await launchTestNode(); + const blobDeployed = (await provider.getBlobs([blobId])).length > 0; + expect(blobDeployed).toBe(true); - const { - wallets: [wallet], - } = launch; - - const factory = new ContractFactory(ScriptMainArgBool.bytecode, ScriptMainArgBool.abi, wallet); - const { waitForResult } = await factory.deployAsBlobTxForScript(); - const { loaderBytecode, configurableOffsetDiff } = await waitForResult(); + expect(loaderScript.bytes).not.toEqual(script.bytes); - const script = new Script( - loaderBytecode, - mapToLoaderAbi(ScriptMainArgBool.abi, configurableOffsetDiff), - wallet - ); - - const { waitForResult: waitForResult2 } = await script.functions.main(true).call(); + const { waitForResult: waitForResult2 } = await loaderScript.functions.main(true).call(); const { value, transactionResult: { transaction }, } = await waitForResult2(); - expect(transaction.script).equals(loaderBytecode); + expect(transaction.script).equals(hexlify(loaderScript.bytes)); expect(value).toBe(true); }); - it('Should work for setting the configurable constants', async () => { + it('script blob deployment works (CONFIGURABLE)', async () => { using launch = await launchTestNode(); const { wallets: [wallet], } = launch; - const factory = new ContractFactory(ScriptDummy.bytecode, ScriptDummy.abi, wallet); - const { waitForResult } = await factory.deployAsBlobTxForScript(); + const script = new ScriptWithConfigurable(wallet); + const loaderScript = await (await script.deploy(wallet)).waitForResult(); - const { loaderBytecode, configurableOffsetDiff } = await waitForResult(); - const script = new Script( - loaderBytecode, - mapToLoaderAbi(ScriptDummy.abi, configurableOffsetDiff), - wallet - ); + expect(decodeConfigurables(loaderScript)).toEqual(decodeConfigurables(script)); - const configurable = { - SECRET_NUMBER: 10001, + // Test that default configurables are included by default + const defaultConfigurable = { + FEE: 5, }; - script.setConfigurableConstants(configurable); - const { waitForResult: waitForResult2 } = await script.functions.main().call(); - const { value } = await waitForResult2(); + const defaultResult = await ( + await loaderScript.functions.main(defaultConfigurable.FEE).call() + ).waitForResult(); - expect(value).toBe(true); - }); - - it('Should return false for incorrectly set configurable constants', async () => { - using launch = await launchTestNode(); + expect(defaultResult.logs[0]).toEqual(defaultConfigurable.FEE); + expect(defaultResult.value).toBe(true); - const { - wallets: [wallet], - } = launch; - - const factory = new ContractFactory(ScriptDummy.bytecode, ScriptDummy.abi, wallet); - const { waitForResult } = await factory.deployAsBlobTxForScript(); - - const { loaderBytecode, configurableOffsetDiff } = await waitForResult(); - - const preScript = new Script( - loaderBytecode, - mapToLoaderAbi(ScriptDummy.abi, configurableOffsetDiff), - wallet - ); + // Test that you can update configurables of the loader script const configurable = { - SECRET_NUMBER: 299, + FEE: 234, }; - preScript.setConfigurableConstants(configurable); - - const { waitForResult: waitForResult2 } = await preScript.functions.main().call(); + loaderScript.setConfigurableConstants(configurable); - const { value, logs } = await waitForResult2(); + const result1 = await ( + await loaderScript.functions.main(configurable.FEE).call() + ).waitForResult(); - expect(logs[0].toNumber()).equal(configurable.SECRET_NUMBER); - expect(value).toBe(false); + expect(result1.logs[0]).toEqual(configurable.FEE); + expect(result1.value).toBe(true); }); - it('it should return false if no configurable constants are set', async () => { + it('script blob deployment works (COMPLEX CONFIGURABLE)', async () => { using launch = await launchTestNode(); const { wallets: [wallet], } = launch; - const factory = new ContractFactory(ScriptDummy.bytecode, ScriptDummy.abi, wallet); - const { waitForResult } = await factory.deployAsBlobTxForScript(); - const { loaderBytecode, configurableOffsetDiff } = await waitForResult(); - - const script = new Script( - loaderBytecode, - mapToLoaderAbi(ScriptDummy.abi, configurableOffsetDiff), - wallet - ); + const script = new ScriptWithMoreConfigurable(wallet); - const { waitForResult: waitForResult2 } = await script.functions.main().call(); - const { value, logs } = await waitForResult2(); - expect(logs[0].toNumber()).equal(9000); - expect(value).toBe(false); - }); - - it('should set configurables in complicated script', async () => { - using launch = await launchTestNode(); - - const { - wallets: [wallet], - } = launch; + const loaderScript = await (await script.deploy(wallet)).waitForResult(); - const factory = new ContractFactory( - ScriptWithMoreConfigurable.bytecode, - ScriptWithMoreConfigurable.abi, - wallet - ); - const { waitForResult } = await factory.deployAsBlobTxForScript(); - const { loaderBytecode, configurableOffsetDiff } = await waitForResult(); + expect(decodeConfigurables(loaderScript)).toEqual(decodeConfigurables(script)); const configurable = { U8: 16, @@ -207,21 +131,10 @@ describe('first try', () => { }, }; - const normalScript = new Script( - ScriptWithMoreConfigurable.bytecode, - ScriptWithMoreConfigurable.abi, - wallet - ); - - normalScript.setConfigurableConstants(configurable); - - const script = new Script( - loaderBytecode, - mapToLoaderAbi(ScriptWithMoreConfigurable.abi, configurableOffsetDiff), - wallet - ); - script.setConfigurableConstants(configurable); + loaderScript.setConfigurableConstants(configurable); + + expect(decodeConfigurables(loaderScript)).toEqual(decodeConfigurables(script)); const { waitForResult: waitForResult2 } = await script.functions.main().call(); const { value, logs } = await waitForResult2(); @@ -241,126 +154,64 @@ describe('first try', () => { expect(logs[10]).toStrictEqual(configurable.STRUCT_1); }); - it('Should work with predicates', async () => { + test("deploying existing script blob doesn't throw", async () => { using launch = await launchTestNode(); + const { - wallets: [wallet], provider, + wallets: [wallet], } = launch; - const receiver = Wallet.generate({ provider }); + const script = new ScriptMainArgBool(wallet); - const factory = new ContractFactory( - PredicateFalseConfigurable.bytecode, - PredicateFalseConfigurable.abi, - wallet - ); - - const { waitForResult } = await factory.deployAsBlobTxForScript(); - const { loaderBytecode, configurableOffsetDiff } = await waitForResult(); - - expect(loaderBytecode).to.not.equal(hexlify(PredicateFalseConfigurable.bytecode)); - - const configurable = { - SECRET_NUMBER: 8000, - }; - - const predicate = new Predicate({ - data: [configurable.SECRET_NUMBER], - bytecode: loaderBytecode, - abi: mapToLoaderAbi(PredicateFalseConfigurable.abi, configurableOffsetDiff), - provider, - configurableConstants: configurable, - }); + const sendTransactionSpy = vi.spyOn(provider, 'sendTransaction'); - await wallet.transfer(predicate.address, 10_000, provider.getBaseAssetId()); + await (await script.deploy(wallet)).waitForResult(); + await (await script.deploy(wallet)).waitForResult(); - const tx = await predicate.transfer(receiver.address, 1000, provider.getBaseAssetId()); - const response = await tx.waitForResult(); - expect(response.isStatusSuccess).toBe(true); + expect(sendTransactionSpy).toHaveBeenCalledTimes(1); }); - it('Should work with predicate when not setting configurables values', async () => { + it('predicate blob deployment works', async () => { using launch = await launchTestNode(); + const { - wallets: [wallet], provider, + wallets: [deployer], } = launch; - const factory = new ContractFactory( - PredicateFalseConfigurable.bytecode, - PredicateFalseConfigurable.abi, - wallet - ); - - const { waitForResult } = await factory.deployAsBlobTxForScript(); - const { loaderBytecode, configurableOffsetDiff } = await waitForResult(); - - expect(loaderBytecode).to.not.equal(hexlify(PredicateFalseConfigurable.bytecode)); - - const SECRET_NUMBER = 9000; + const configurableConstants = { + FEE: 10, + ADDRESS: '0x38966262edb5997574be45f94c665aedb41a1663f5b0528e765f355086eebf96', + }; - const predicate = new Predicate({ - data: [bn(SECRET_NUMBER)], - bytecode: loaderBytecode, - abi: mapToLoaderAbi(PredicateFalseConfigurable.abi, configurableOffsetDiff), + const predicate = new PredicateWithConfigurable({ provider, + configurableConstants, + data: [configurableConstants.FEE, configurableConstants.ADDRESS], }); - const transfer2 = await wallet.transfer(predicate.address, 10_000, provider.getBaseAssetId()); - await transfer2.waitForResult(); + const { waitForResult, blobId } = await predicate.deploy(deployer); + const loaderPredicate = await waitForResult(); - /** - * When destructuring the response, we get the following error: - * Cannot read properties of undefined (reading 'waitForStatusChange') - * TODO: Fix this! - */ - const transfer3 = await predicate.transfer(wallet.address, 1000, provider.getBaseAssetId()); - const { isStatusSuccess } = await transfer3.waitForResult(); + const blobDeployed = (await provider.getBlobs([blobId])).length > 0; + expect(blobDeployed).toBe(true); - expect(isStatusSuccess).toBe(true); - }); + expect(loaderPredicate.bytes).not.toEqual(predicate.bytes); + expect(loaderPredicate.address.toB256()).not.toEqual(predicate.address.toB256()); + expect(loaderPredicate.predicateData).toEqual(predicate.predicateData); + expect(decodeConfigurables(loaderPredicate)).toEqual(decodeConfigurables(predicate)); - it('Should work with predicate with no configurable constants', async () => { - using launch = await launchTestNode(); - const { - wallets: [wallet], - provider, - } = launch; - - const factory = new ContractFactory( - PredicateFalseConfigurable.bytecode, - PredicateFalseConfigurable.abi, - wallet - ); + await (await deployer.transfer(loaderPredicate.address, 10_000)).waitForResult(); - const { waitForResult } = await factory.deployAsBlobTxForScript(); - const { loaderBytecode } = await waitForResult(); - - expect(loaderBytecode).to.not.equal(hexlify(PredicateTrue.bytecode)); - - const predicate = new Predicate({ - bytecode: PredicateTrue.bytecode, - abi: PredicateTrue.abi, - provider, - loaderBytecode, - }); - - const transfer2 = await wallet.transfer(predicate.address, 10_000, provider.getBaseAssetId()); - await transfer2.waitForResult(); - - /** - * When destructuring the response, we get the following error: - * Cannot read properties of undefined (reading 'waitForStatusChange') - * TODO: Fix this! - */ - const transfer3 = await predicate.transfer(wallet.address, 1000, provider.getBaseAssetId()); - const { isStatusSuccess } = await transfer3.waitForResult(); + const { isStatusSuccess } = await ( + await loaderPredicate.transfer(deployer.address, 10) + ).waitForResult(); expect(isStatusSuccess).toBe(true); }); - it('can run with loader bytecode with manually modified configurables', async () => { + it('loader predicate can be used with different configurables', async () => { using launch = await launchTestNode(); const { wallets: [wallet], @@ -369,26 +220,23 @@ describe('first try', () => { const receiver = Wallet.generate({ provider }); - const factory = new ContractFactory( - PredicateFalseConfigurable.bytecode, - PredicateFalseConfigurable.abi, - wallet - ); - - const { waitForResult } = await factory.deployAsBlobTxForScript(); - const { loaderBytecode, configurableOffsetDiff } = await waitForResult(); - expect(loaderBytecode).to.not.equal(hexlify(PredicateFalseConfigurable.bytecode)); + const loaderPredicate = await ( + await new PredicateWithMoreConfigurables({ provider }).deploy(wallet) + ).waitForResult(); const configurable = { - SECRET_NUMBER: 8000, + FEE: 99, + ADDRESS: getRandomB256(), + U16: 305, + U32: 101, + U64: 1000000, + BOOL: false, }; - const newAbi = mapToLoaderAbi(PredicateFalseConfigurable.abi, configurableOffsetDiff); - const predicate = new Predicate({ - data: [configurable.SECRET_NUMBER], - bytecode: loaderBytecode, - abi: newAbi, + data: [configurable.FEE, configurable.ADDRESS], + bytecode: loaderPredicate.bytes, + abi: loaderPredicate.interface.jsonAbi, provider, configurableConstants: configurable, }); @@ -400,47 +248,23 @@ describe('first try', () => { expect(response.isStatusSuccess).toBe(true); }); - it('can run with loader bytecode with many manually modified configurables', async () => { + test("deploying existing predicate blob doesn't throw", async () => { using launch = await launchTestNode(); + const { - wallets: [wallet], provider, + wallets: [deployer], } = launch; - const receiver = Wallet.generate({ provider }); - - const factory = new ContractFactory( - PredicateWithMoreConfigurables.bytecode, - PredicateWithMoreConfigurables.abi, - wallet - ); - - const { waitForResult } = await factory.deployAsBlobTxForScript(); - const { loaderBytecode, configurableOffsetDiff } = await waitForResult(); - expect(loaderBytecode).to.not.equal(hexlify(PredicateWithMoreConfigurables.bytecode)); - const configurable = { - FEE: 99, - ADDRESS: getRandomB256(), - U16: 305, - U32: 101, - U64: 1000000, - BOOL: false, - }; - - const newAbi = mapToLoaderAbi(PredicateWithMoreConfigurables.abi, configurableOffsetDiff); - - const predicate = new Predicate({ - data: [configurable.FEE, configurable.ADDRESS], - bytecode: loaderBytecode, - abi: newAbi, + const predicate = new PredicateTrue({ provider, - configurableConstants: configurable, }); - await wallet.transfer(predicate.address, 10_000, provider.getBaseAssetId()); + const sendTransactionSpy = vi.spyOn(provider, 'sendTransaction'); - const tx = await predicate.transfer(receiver.address, 1000, provider.getBaseAssetId()); - const response = await tx.waitForResult(); - expect(response.isStatusSuccess).toBe(true); + await (await predicate.deploy(deployer)).waitForResult(); + await (await predicate.deploy(deployer)).waitForResult(); + + expect(sendTransactionSpy).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/fuel-gauge/src/check-user-account.test.ts b/packages/fuel-gauge/src/check-user-account.test.ts index de623dfd6f6..e007b2f8508 100644 --- a/packages/fuel-gauge/src/check-user-account.test.ts +++ b/packages/fuel-gauge/src/check-user-account.test.ts @@ -1,8 +1,7 @@ -import { ContractFactory } from 'fuels'; import { launchTestNode } from 'fuels/test-utils'; import { AdvancedLoggingFactory } from '../test/typegen/contracts/AdvancedLoggingFactory'; -import { ScriptDummy } from '../test/typegen/scripts'; +import { ScriptMainArgBool } from '../test/typegen/scripts'; /** * @group browser @@ -17,8 +16,8 @@ describe('User account tests', () => { wallets: [wallet], } = launch; - const factory = new ContractFactory(ScriptDummy.bytecode, ScriptDummy.abi, wallet); - const { waitForResult, blobId } = await factory.deployAsBlobTxForScript(); + const script = new ScriptMainArgBool(wallet); + const { waitForResult, blobId } = await script.deploy(wallet); await waitForResult(); diff --git a/packages/fuel-gauge/src/contract.test.ts b/packages/fuel-gauge/src/contract.test.ts index fc962e6a78e..37a4e9f717d 100644 --- a/packages/fuel-gauge/src/contract.test.ts +++ b/packages/fuel-gauge/src/contract.test.ts @@ -10,7 +10,6 @@ import { transactionRequestify, Wallet, ContractFactory, - Predicate, PolicyType, buildFunctionResult, ReceiptType, @@ -879,10 +878,7 @@ describe('Contract', () => { const amountToContract = 200; const amountToPredicate = 500_000; - const predicate = new Predicate({ - bytecode: PredicateTrue.bytecode, - provider, - }); + const predicate = new PredicateTrue({ provider }); const tx1 = await wallet.transfer( predicate.address, diff --git a/packages/fuel-gauge/src/doc-examples.test.ts b/packages/fuel-gauge/src/doc-examples.test.ts index e718077f1ef..535e6378ae7 100644 --- a/packages/fuel-gauge/src/doc-examples.test.ts +++ b/packages/fuel-gauge/src/doc-examples.test.ts @@ -249,6 +249,7 @@ describe('Doc Examples', () => { const { provider } = launched; const predicate = new Predicate({ bytecode: PredicateTrue.bytecode, + abi: PredicateTrue.abi, provider, }); diff --git a/packages/fuel-gauge/src/predicate/predicate-configurables.test.ts b/packages/fuel-gauge/src/predicate/predicate-configurables.test.ts index e50a9da0905..582f757e1a2 100644 --- a/packages/fuel-gauge/src/predicate/predicate-configurables.test.ts +++ b/packages/fuel-gauge/src/predicate/predicate-configurables.test.ts @@ -1,4 +1,4 @@ -import { getRandomB256, WalletUnlocked, Predicate, FuelError } from 'fuels'; +import { getRandomB256, WalletUnlocked, FuelError } from 'fuels'; import { expectToThrowFuelError, launchTestNode } from 'fuels/test-utils'; import { PredicateTrue, PredicateWithConfigurable } from '../../test/typegen'; @@ -26,9 +26,7 @@ describe('Predicate', () => { wallets: [wallet], } = launched; - const predicate = new Predicate({ - abi: PredicateWithConfigurable.abi, - bytecode: PredicateWithConfigurable.bytecode, + const predicate = new PredicateWithConfigurable({ provider: wallet.provider, data: [defaultValues.FEE, defaultValues.ADDRESS], // set predicate input data to be the same as default configurable value }); @@ -69,9 +67,7 @@ describe('Predicate', () => { const configurableConstants = { FEE: 35 }; expect(configurableConstants.FEE).not.toEqual(defaultValues.FEE); - const predicate = new Predicate({ - abi: PredicateWithConfigurable.abi, - bytecode: PredicateWithConfigurable.bytecode, + const predicate = new PredicateWithConfigurable({ provider, data: [configurableConstants.FEE, defaultValues.ADDRESS], configurableConstants, @@ -114,9 +110,7 @@ describe('Predicate', () => { const configurableConstants = { ADDRESS: getRandomB256() }; expect(configurableConstants.ADDRESS).not.toEqual(defaultValues.ADDRESS); - const predicate = new Predicate({ - abi: PredicateWithConfigurable.abi, - bytecode: PredicateWithConfigurable.bytecode, + const predicate = new PredicateWithConfigurable({ provider, data: [defaultValues.FEE, configurableConstants.ADDRESS], configurableConstants, @@ -163,9 +157,7 @@ describe('Predicate', () => { expect(configurableConstants.FEE).not.toEqual(defaultValues.FEE); expect(configurableConstants.ADDRESS).not.toEqual(defaultValues.ADDRESS); - const predicate = new Predicate({ - abi: PredicateWithConfigurable.abi, - bytecode: PredicateWithConfigurable.bytecode, + const predicate = new PredicateWithConfigurable({ provider, data: [configurableConstants.FEE, configurableConstants.ADDRESS], configurableConstants, @@ -245,9 +237,7 @@ describe('Predicate', () => { wallets: [wallet], } = launched; - const predicate = new Predicate({ - abi: PredicateWithConfigurable.abi, - bytecode: PredicateWithConfigurable.bytecode, + const predicate = new PredicateWithConfigurable({ provider, }); @@ -269,11 +259,9 @@ describe('Predicate', () => { await expectToThrowFuelError( () => - new Predicate({ - bytecode: PredicateTrue.bytecode, - abi: PredicateTrue.abi, + new PredicateTrue({ provider, - data: ['NADA'], + // @ts-expect-error testing invalid configurable configurableConstants: { constant: 'NADA', }, @@ -292,12 +280,10 @@ describe('Predicate', () => { await expectToThrowFuelError( () => - new Predicate({ - bytecode: PredicateWithConfigurable.bytecode, - abi: PredicateWithConfigurable.abi, + new PredicateWithConfigurable({ provider, - data: ['NADA'], configurableConstants: { + // @ts-expect-error testing invalid configurable NOPE: 'NADA', }, }), @@ -307,27 +293,5 @@ describe('Predicate', () => { ) ); }); - - it('throws when setting a configurable with no ABI', async () => { - using launched = await launchTestNode(); - - const { provider } = launched; - - await expectToThrowFuelError( - () => - new Predicate({ - bytecode: PredicateWithConfigurable.bytecode, - provider, - data: ['NADA'], - configurableConstants: { - NOPE: 'NADA', - }, - }), - new FuelError( - FuelError.CODES.INVALID_CONFIGURABLE_CONSTANTS, - `Error setting configurable constants: Cannot validate configurable constants because the Predicate was instantiated without a JSON ABI.` - ) - ); - }); }); }); diff --git a/packages/fuel-gauge/src/transaction-summary.test.ts b/packages/fuel-gauge/src/transaction-summary.test.ts index 4943039e24f..c8476575a2d 100644 --- a/packages/fuel-gauge/src/transaction-summary.test.ts +++ b/packages/fuel-gauge/src/transaction-summary.test.ts @@ -52,7 +52,7 @@ function convertBnsToHex(value: unknown): unknown { * @group browser */ describe('TransactionSummary', () => { - const verifyTransactionSummary = (params: { + const validateTxSummary = (params: { transaction: TransactionResult | TransactionSummary; isRequest?: boolean; }) => { @@ -71,7 +71,6 @@ describe('TransactionSummary', () => { expect(transaction.isStatusSuccess).toBe(!isRequest); expect(transaction.isStatusPending).toBe(false); if (!isRequest) { - expect(transaction.blockId).toEqual(expect.any(String)); expect(transaction.time).toEqual(expect.any(String)); expect(transaction.status).toEqual(expect.any(String)); expect(transaction.date).toEqual(expect.any(Date)); @@ -83,42 +82,34 @@ describe('TransactionSummary', () => { const { provider, - wallets: [adminWallet], + wallets: [sender, destination], } = launched; - const destination = Wallet.generate({ + const tx = await sender.transfer(destination.address, 1000, provider.getBaseAssetId()); + const submittedTxResult = await tx.waitForResult(); + + const laterFetchedResult = await getTransactionSummary({ + id: tx.id, provider, }); - const amountToTransfer = 100; - - const request = new ScriptTransactionRequest({ - gasLimit: 10000, + validateTxSummary({ + transaction: submittedTxResult, }); - request.addCoinOutput(destination.address, amountToTransfer, provider.getBaseAssetId()); - - const txCost = await adminWallet.getTransactionCost(request); - - request.gasLimit = txCost.gasUsed; - request.maxFee = txCost.maxFee; - - await adminWallet.fund(request, txCost); - - const tx = await adminWallet.sendTransaction(request); - - const transactionResponse = await tx.waitForResult(); - - const transactionSummary = await getTransactionSummary({ - id: tx.id, - provider, + validateTxSummary({ + transaction: laterFetchedResult, }); - verifyTransactionSummary({ - transaction: transactionSummary, - }); + /** + * Ensure both the original response and the subsequently fetched singular response + * contain the blockId + */ + expect(submittedTxResult.blockId).toBeDefined(); + expect(laterFetchedResult.blockId).toBeDefined(); - expect(convertBnsToHex(transactionResponse)).toStrictEqual(convertBnsToHex(transactionSummary)); + // Ensure the two responses are the same + expect(convertBnsToHex(submittedTxResult)).toStrictEqual(convertBnsToHex(laterFetchedResult)); }); it('should ensure getTransactionsSummaries executes just fine', async () => { @@ -126,35 +117,22 @@ describe('TransactionSummary', () => { const { provider, - wallets: [adminWallet], + wallets: [sender, destination], } = launched; - const sender = Wallet.generate({ - provider, - }); - - const tx1 = await adminWallet.transfer(sender.address, 500_000, provider.getBaseAssetId(), { - gasLimit: 10_000, - }); - const transactionResponse1 = await tx1.waitForResult(); - const amountToTransfer = 100; - const destination = Wallet.generate({ - provider, - }); + const tx1 = await sender.transfer(sender.address, amountToTransfer, provider.getBaseAssetId()); + const txResult1 = await tx1.waitForResult(); const tx2 = await sender.transfer( destination.address, - amountToTransfer, - provider.getBaseAssetId(), - { - gasLimit: 10_000, - } + amountToTransfer * 2, + provider.getBaseAssetId() ); - const transactionResponse2 = await tx2.waitForResult(); + const txResult2 = await tx2.waitForResult(); - const { transactions } = await getTransactionsSummaries({ + const { transactions: submittedTxResult } = await getTransactionsSummaries({ provider, filters: { first: 2, @@ -162,44 +140,57 @@ describe('TransactionSummary', () => { }, }); - expect(transactions.length).toBe(2); + expect(submittedTxResult.length).toBe(2); - transactions.forEach((transactionSummary) => { - verifyTransactionSummary({ + submittedTxResult.forEach((transactionSummary) => { + validateTxSummary({ transaction: transactionSummary, }); }); - expect(convertBnsToHex(transactions[0])).toStrictEqual(convertBnsToHex(transactionResponse1)); - expect(convertBnsToHex(transactions[1])).toStrictEqual(convertBnsToHex(transactionResponse2)); + expect(txResult1.blockId).toBeDefined(); + expect(txResult2.blockId).toBeDefined(); + + /** + * When fetching list of transactions, the blockId is not returned + */ + expect(convertBnsToHex(submittedTxResult[0])).toStrictEqual({ + ...(convertBnsToHex(txResult1) as TransactionResult), + blockId: undefined, + }); + expect(convertBnsToHex(submittedTxResult[1])).toStrictEqual({ + ...(convertBnsToHex(txResult2) as TransactionResult), + blockId: undefined, + }); }); - it('should ensure getTransactionSummaryFromRequest executes just fine', async () => { + it('should ensure getTransactionSummaryFromRequest executes just fine [TX REQUEST]', async () => { using launched = await launchTestNode(); const { provider, - wallets: [adminWallet], + wallets: [sender], } = launched; const request = new ScriptTransactionRequest({ gasLimit: 10000, }); - const txCost = await adminWallet.getTransactionCost(request); + const txCost = await sender.getTransactionCost(request); request.gasLimit = txCost.gasUsed; request.maxFee = txCost.maxFee; - await adminWallet.fund(request, txCost); + await sender.fund(request, txCost); - const transactionRequest = await adminWallet.populateTransactionWitnessesSignature(request); + const transactionRequest = await sender.populateTransactionWitnessesSignature(request); const transactionSummary = await getTransactionSummaryFromRequest({ provider, transactionRequest, }); - verifyTransactionSummary({ + + validateTxSummary({ transaction: transactionSummary, isRequest: true, }); @@ -207,6 +198,53 @@ describe('TransactionSummary', () => { expect(transactionSummary.transaction).toStrictEqual(transactionRequest.toTransaction()); }); + it('should ensure submitted transaction returns block ID', async () => { + using launched = await launchTestNode(); + + const { + wallets: [adminWallet, receiver], + } = launched; + + const submitted = await adminWallet.transfer(receiver.address, 1000, ASSET_A); + const response = await submitted.waitForResult(); + + validateTxSummary({ + transaction: response, + }); + + expect(response.blockId).toBeDefined(); + expect(response.blockId).toEqual(expect.any(String)); + }); + + it('should ensure listed TX summaries do not include block ID', async () => { + using launched = await launchTestNode(); + + const { + provider, + wallets: [sender, receiver], + } = launched; + + const length = 5; + + for (let i = 0; i < length; i++) { + const submitted = await sender.transfer(receiver.address, 1000, ASSET_A); + await submitted.waitForResult(); + } + + const { transactions } = await getTransactionsSummaries({ + provider, + filters: { + owner: sender.address.toB256(), + first: 50, + }, + }); + + expect(transactions).toHaveLength(length); + transactions.forEach((transaction) => { + expect(transaction.blockId).toBeUndefined(); + }); + }); + describe('Transfer Operations', () => { const validateTransferOperation = (params: { operations: Operation[]; @@ -225,9 +263,9 @@ describe('TransactionSummary', () => { expect(operations[index].to?.address).toBe(address.toB256()); expect(operations[index].assetsSent).toHaveLength(quantities.length); - quantities.forEach(({ amount, assetId }, qunatitiesIndex) => { - expect(Number(operations[index].assetsSent?.[qunatitiesIndex].amount)).toBe(amount); - expect(operations[index].assetsSent?.[qunatitiesIndex].assetId).toBe(assetId); + quantities.forEach(({ amount, assetId }, quantitiesIndex) => { + expect(Number(operations[index].assetsSent?.[quantitiesIndex].amount)).toBe(amount); + expect(operations[index].assetsSent?.[quantitiesIndex].assetId).toBe(assetId); }); }); }; @@ -268,7 +306,7 @@ describe('TransactionSummary', () => { const transferAmount = 1233; const minorAmount = 1000; const majorAmount = 100_000_000_000; - const tranferBackAmount = majorAmount - 10_000; + const transferBackAmount = majorAmount - 10_000; using launched = await launchTestNode({ walletsConfig: { @@ -303,11 +341,11 @@ describe('TransactionSummary', () => { }); request.addResources([...majorResources, ...minorResources]); - // Add tranfer to recipient + // Add transfer to recipient request.addCoinOutput(recipient.address, transferAmount, provider.getBaseAssetId()); // Add transfer to self - request.addCoinOutput(majorWallet.address, tranferBackAmount, provider.getBaseAssetId()); + request.addCoinOutput(majorWallet.address, transferBackAmount, provider.getBaseAssetId()); // Explicitly setting the Output Change address to the recipient const index = request.outputs.findIndex((output) => output.type === OutputType.Change); @@ -331,7 +369,7 @@ describe('TransactionSummary', () => { }, { address: majorWallet.address, - quantities: [{ amount: tranferBackAmount, assetId: provider.getBaseAssetId() }], + quantities: [{ amount: transferBackAmount, assetId: provider.getBaseAssetId() }], }, ], }); diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml b/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml index bb2f195d352..f76d1d3d097 100644 --- a/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml +++ b/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml @@ -8,7 +8,6 @@ members = [ "auth_testing_contract", "bytecode-sway-lib", "bytes-contract", - "script-dummy", "call-test-contract", "collision_in_fn_names", "complex-predicate", diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/script-dummy/Forc.toml b/packages/fuel-gauge/test/fixtures/forc-projects/script-dummy/Forc.toml deleted file mode 100644 index 2e82eed1125..00000000000 --- a/packages/fuel-gauge/test/fixtures/forc-projects/script-dummy/Forc.toml +++ /dev/null @@ -1,7 +0,0 @@ -[project] -authors = ["Fuel Labs "] -entry = "main.sw" -license = "Apache-2.0" -name = "script-dummy" - -[dependencies] diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/script-dummy/src/main.sw b/packages/fuel-gauge/test/fixtures/forc-projects/script-dummy/src/main.sw deleted file mode 100644 index af21ada4589..00000000000 --- a/packages/fuel-gauge/test/fixtures/forc-projects/script-dummy/src/main.sw +++ /dev/null @@ -1,9 +0,0 @@ -script; -configurable { - SECRET_NUMBER: u64 = 9000, -} - -fn main() -> bool { - log(SECRET_NUMBER); - SECRET_NUMBER == 10001 -} diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/script-with-configurable/src/main.sw b/packages/fuel-gauge/test/fixtures/forc-projects/script-with-configurable/src/main.sw index d84adc07c73..2c5ee67efd9 100644 --- a/packages/fuel-gauge/test/fixtures/forc-projects/script-with-configurable/src/main.sw +++ b/packages/fuel-gauge/test/fixtures/forc-projects/script-with-configurable/src/main.sw @@ -5,5 +5,6 @@ configurable { } fn main(inputed_fee: u8) -> bool { + log(FEE); FEE == inputed_fee } diff --git a/packages/fuels/src/cli/commands/deploy/adjustOffsets.ts b/packages/fuels/src/cli/commands/deploy/adjustOffsets.ts deleted file mode 100644 index bb0056df036..00000000000 --- a/packages/fuels/src/cli/commands/deploy/adjustOffsets.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { JsonAbi } from '@fuel-ts/abi-coder'; - -export const adjustOffsets = (jsonAbi: JsonAbi, configurableOffsetDiff: number) => { - const { configurables: readOnlyConfigurables } = jsonAbi; - const configurables: JsonAbi['configurables'] = []; - readOnlyConfigurables.forEach((config) => { - // @ts-expect-error shut up the read-only thing - configurables.push({ ...config, offset: config.offset - configurableOffsetDiff }); - }); - return { ...jsonAbi, configurables } as JsonAbi; -}; diff --git a/packages/fuels/src/cli/commands/deploy/deployPredicates.ts b/packages/fuels/src/cli/commands/deploy/deployPredicates.ts index 3010841ff5b..b3375d85e42 100644 --- a/packages/fuels/src/cli/commands/deploy/deployPredicates.ts +++ b/packages/fuels/src/cli/commands/deploy/deployPredicates.ts @@ -1,36 +1,12 @@ -import type { JsonAbi } from '@fuel-ts/abi-coder'; -import { getPredicateRoot, type WalletUnlocked } from '@fuel-ts/account'; -import { ContractFactory } from '@fuel-ts/contract'; -import { arrayify } from '@fuel-ts/utils'; +import { getPredicateRoot, Predicate } from '@fuel-ts/account'; import { debug, log } from 'console'; import { readFileSync } from 'fs'; import { getABIPath, getBinaryPath, getPredicateName } from '../../config/forcUtils'; import type { DeployedPredicate, FuelsConfig } from '../../types'; -import { adjustOffsets } from './adjustOffsets'; import { createWallet } from './createWallet'; -/** - * Deploys one predicate. - */ -export async function deployPredicate(wallet: WalletUnlocked, binaryPath: string, abiPath: string) { - debug(`Deploying predicate for ABI: ${abiPath}`); - - const bytecode = readFileSync(binaryPath); - const abi = JSON.parse(readFileSync(abiPath, 'utf-8')); - const factory = new ContractFactory(bytecode, abi, wallet); - - const { waitForResult } = await factory.deployAsBlobTxForScript(); - - const { loaderBytecode, configurableOffsetDiff } = await waitForResult(); - - return { - loaderBytecode, - configurableOffsetDiff, - }; -} - /** * Deploys all predicates. */ @@ -48,26 +24,24 @@ export async function deployPredicates(config: FuelsConfig) { const binaryPath = getBinaryPath(predicatePath, config); const abiPath = getABIPath(predicatePath, config); const projectName = getPredicateName(predicatePath); + const bytecode = readFileSync(binaryPath); + const abi = JSON.parse(readFileSync(abiPath, 'utf-8')); - const { loaderBytecode, configurableOffsetDiff } = await deployPredicate( - wallet, - binaryPath, - abiPath - ); - const predicateRoot = getPredicateRoot(loaderBytecode); + const predicate = new Predicate({ abi, bytecode, provider: wallet.provider }); + const { + bytes: loaderBytecode, + interface: { jsonAbi }, + } = await (await predicate.deploy(wallet)).waitForResult(); - let abi = JSON.parse(readFileSync(abiPath, 'utf-8')) as JsonAbi; - if (configurableOffsetDiff) { - abi = adjustOffsets(abi, configurableOffsetDiff); - } + const predicateRoot = getPredicateRoot(loaderBytecode); debug(`Predicate deployed: ${projectName} - ${predicateRoot}`); predicates.push({ path: predicatePath, predicateRoot, - loaderBytecode: arrayify(loaderBytecode), - abi, + loaderBytecode, + abi: jsonAbi, }); } diff --git a/packages/fuels/src/cli/commands/deploy/deployScripts.ts b/packages/fuels/src/cli/commands/deploy/deployScripts.ts index 62bdb9c386d..557536d5987 100644 --- a/packages/fuels/src/cli/commands/deploy/deployScripts.ts +++ b/packages/fuels/src/cli/commands/deploy/deployScripts.ts @@ -1,36 +1,12 @@ -import type { JsonAbi } from '@fuel-ts/abi-coder'; -import type { WalletUnlocked } from '@fuel-ts/account'; -import { ContractFactory } from '@fuel-ts/contract'; -import { arrayify } from '@fuel-ts/utils'; +import { Script } from '@fuel-ts/script'; import { debug, log } from 'console'; import { readFileSync } from 'fs'; import { getBinaryPath, getABIPath, getScriptName } from '../../config/forcUtils'; import type { FuelsConfig, DeployedScript } from '../../types'; -import { adjustOffsets } from './adjustOffsets'; import { createWallet } from './createWallet'; -/** - * Deploys one script. - */ -export async function deployScript(wallet: WalletUnlocked, binaryPath: string, abiPath: string) { - debug(`Deploying script for ABI: ${abiPath}`); - - const bytecode = readFileSync(binaryPath); - const abi = JSON.parse(readFileSync(abiPath, 'utf-8')); - const factory = new ContractFactory(bytecode, abi, wallet); - - const { waitForResult, blobId } = await factory.deployAsBlobTxForScript(); - const { configurableOffsetDiff, loaderBytecode } = await waitForResult(); - - return { - blobId, - loaderBytecode, - configurableOffsetDiff, - }; -} - /** * Deploys all scripts. */ @@ -49,23 +25,21 @@ export async function deployScripts(config: FuelsConfig) { const abiPath = getABIPath(scriptPath, config); const projectName = getScriptName(scriptPath); - const { blobId, loaderBytecode, configurableOffsetDiff } = await deployScript( - wallet, - binaryPath, - abiPath - ); + const bytecode = readFileSync(binaryPath); + const abi = JSON.parse(readFileSync(abiPath, 'utf-8')); - let abi = JSON.parse(readFileSync(abiPath, 'utf-8')) as JsonAbi; - if (configurableOffsetDiff) { - abi = adjustOffsets(abi, configurableOffsetDiff); - } + const script = new Script(bytecode, abi, wallet); + const { + bytes: loaderBytecode, + interface: { jsonAbi }, + } = await (await script.deploy(wallet)).waitForResult(); - debug(`Script deployed: ${projectName} - ${blobId}`); + debug(`Script deployed: ${projectName}`); scripts.push({ path: scriptPath, - loaderBytecode: arrayify(loaderBytecode), - abi, + loaderBytecode, + abi: jsonAbi, }); } diff --git a/packages/fuels/src/cli/commands/deploy/index.test.ts b/packages/fuels/src/cli/commands/deploy/index.test.ts index c2f05420328..c36c4f39a67 100644 --- a/packages/fuels/src/cli/commands/deploy/index.test.ts +++ b/packages/fuels/src/cli/commands/deploy/index.test.ts @@ -2,7 +2,7 @@ import { Wallet } from '@fuel-ts/account'; import { fuelsConfig } from '../../../../test/fixtures/fuels.config'; import { launchTestNode } from '../../../test-utils'; -import type { DeployedContract } from '../../types'; +import type { DeployedData } from '../../types'; import { deploy } from '.'; import * as createWalletMod from './createWallet'; @@ -32,11 +32,16 @@ describe('deploy', () => { // TODO: Fix this test test.skip('should call onDeploy callback', async () => { const { onDeploy } = await mockAll(); - const expectedContracts: DeployedContract[] = []; - const config = { ...fuelsConfig, contracts: [], onDeploy }; + const expectedData: DeployedData = { + contracts: [], + scripts: [], + predicates: [], + }; + + const config = { ...fuelsConfig, contracts: [], scripts: [], predicates: [], onDeploy }; await deploy(config); - expect(onDeploy).toHaveBeenCalledWith(config, expectedContracts); + expect(onDeploy).toHaveBeenCalledWith(config, expectedData); }); }); diff --git a/packages/fuels/src/cli/commands/deploy/index.ts b/packages/fuels/src/cli/commands/deploy/index.ts index 536c5992b13..28b14181d41 100644 --- a/packages/fuels/src/cli/commands/deploy/index.ts +++ b/packages/fuels/src/cli/commands/deploy/index.ts @@ -12,10 +12,8 @@ export async function deploy(config: FuelsConfig) { /** * Deploy contract and save their IDs to JSON file. */ - const contractIds = await deployContracts(config); - await saveContractIds(contractIds, config.output); - - config.onDeploy?.(config, contractIds); + const contracts = await deployContracts(config); + await saveContractIds(contracts, config.output); /** * Deploy scripts and save deployed files to disk. @@ -29,11 +27,21 @@ export async function deploy(config: FuelsConfig) { const predicates = await deployPredicates(config); savePredicateFiles(predicates, config); + config.onDeploy?.(config, { + contracts, + scripts, + predicates, + }); + /** * After deploying scripts/predicates, we need to * re-generate factory classe with the loader coee */ await generateTypes(config); - return contractIds; + return { + contracts, + scripts, + predicates, + }; } diff --git a/packages/fuels/src/cli/commands/deploy/proxy/types/Src14OwnedProxy.ts b/packages/fuels/src/cli/commands/deploy/proxy/types/Src14OwnedProxy.ts index fbb824b1e33..ceaf03af0ee 100644 --- a/packages/fuels/src/cli/commands/deploy/proxy/types/Src14OwnedProxy.ts +++ b/packages/fuels/src/cli/commands/deploy/proxy/types/Src14OwnedProxy.ts @@ -5,7 +5,7 @@ /* eslint-disable @typescript-eslint/consistent-type-imports */ /* - Fuels version: 0.96.0 + Fuels version: 0.96.1 */ import { Contract, Interface } from "../../../../.."; diff --git a/packages/fuels/src/cli/commands/deploy/proxy/types/Src14OwnedProxyFactory.ts b/packages/fuels/src/cli/commands/deploy/proxy/types/Src14OwnedProxyFactory.ts index fb154d531f7..c51e0bd0930 100644 --- a/packages/fuels/src/cli/commands/deploy/proxy/types/Src14OwnedProxyFactory.ts +++ b/packages/fuels/src/cli/commands/deploy/proxy/types/Src14OwnedProxyFactory.ts @@ -5,7 +5,7 @@ /* eslint-disable @typescript-eslint/consistent-type-imports */ /* - Fuels version: 0.96.0 + Fuels version: 0.96.1 */ import { Contract, ContractFactory, decompressBytecode } from "../../../../.."; diff --git a/packages/fuels/src/cli/commands/deploy/proxy/types/common.d.ts b/packages/fuels/src/cli/commands/deploy/proxy/types/common.d.ts index c95fb406118..0620f9d4b29 100644 --- a/packages/fuels/src/cli/commands/deploy/proxy/types/common.d.ts +++ b/packages/fuels/src/cli/commands/deploy/proxy/types/common.d.ts @@ -5,7 +5,7 @@ /* eslint-disable @typescript-eslint/consistent-type-imports */ /* - Fuels version: 0.96.0 + Fuels version: 0.96.1 */ /** diff --git a/packages/fuels/src/cli/commands/deploy/proxy/types/index.ts b/packages/fuels/src/cli/commands/deploy/proxy/types/index.ts index 8e1185e2092..15580357b5e 100644 --- a/packages/fuels/src/cli/commands/deploy/proxy/types/index.ts +++ b/packages/fuels/src/cli/commands/deploy/proxy/types/index.ts @@ -5,7 +5,7 @@ /* eslint-disable @typescript-eslint/consistent-type-imports */ /* - Fuels version: 0.96.0 + Fuels version: 0.96.1 */ export { Src14OwnedProxy } from './Src14OwnedProxy'; diff --git a/packages/fuels/src/cli/commands/dev/index.test.ts b/packages/fuels/src/cli/commands/dev/index.test.ts index 2dccd88575b..f950daff5b4 100644 --- a/packages/fuels/src/cli/commands/dev/index.test.ts +++ b/packages/fuels/src/cli/commands/dev/index.test.ts @@ -42,7 +42,11 @@ describe('dev', () => { .mockReturnValue(Promise.resolve(fuelsConfig)); const build = vi.spyOn(buildMod, 'build').mockResolvedValue(); - const deploy = vi.spyOn(deployMod, 'deploy').mockResolvedValue([]); + const deploy = vi.spyOn(deployMod, 'deploy').mockResolvedValue({ + contracts: [], + scripts: [], + predicates: [], + }); return { autoStartFuelCore, diff --git a/packages/fuels/src/cli/commands/withConfig.test.ts b/packages/fuels/src/cli/commands/withConfig.test.ts index 7bef6dcafc7..cff61a5c9bb 100644 --- a/packages/fuels/src/cli/commands/withConfig.test.ts +++ b/packages/fuels/src/cli/commands/withConfig.test.ts @@ -45,7 +45,11 @@ describe('withConfig', () => { if (params?.shouldErrorOnDeploy) { throw new Error('Something happened'); } - return Promise.resolve([]); + return Promise.resolve({ + contracts: [], + scripts: [], + predicates: [], + }); }); const { error } = mockLogger(); diff --git a/packages/fuels/src/cli/types.ts b/packages/fuels/src/cli/types.ts index 2c5ed7c0ea8..f44c32cd93e 100644 --- a/packages/fuels/src/cli/types.ts +++ b/packages/fuels/src/cli/types.ts @@ -17,7 +17,7 @@ export type CommandEvent = } | { type: Commands.deploy; - data: DeployedContract[]; + data: DeployedData; } | { type: Commands.dev; @@ -51,6 +51,12 @@ export type DeployedPredicate = DeployedScript & { predicateRoot: string; }; +export type DeployedData = { + contracts?: DeployedContract[]; + scripts?: DeployedScript[]; + predicates?: DeployedPredicate[]; +}; + export type ContractDeployOptions = { contracts: DeployedContract[]; contractName: string; diff --git a/packages/fuels/test/features/build.test.ts b/packages/fuels/test/features/build.test.ts index 07ba32c8f40..4c2956c1c9f 100644 --- a/packages/fuels/test/features/build.test.ts +++ b/packages/fuels/test/features/build.test.ts @@ -32,7 +32,11 @@ describe('build', { timeout: 180000 }, () => { function mockAll() { const { autoStartFuelCore, killChildProcess } = mockStartFuelCore(); - const deploy = vi.spyOn(deployMod, 'deploy').mockResolvedValue([]); + const deploy = vi.spyOn(deployMod, 'deploy').mockResolvedValue({ + contracts: [], + scripts: [], + predicates: [], + }); return { autoStartFuelCore, killChildProcess, deploy }; } diff --git a/packages/fuels/test/features/dev.test.ts b/packages/fuels/test/features/dev.test.ts index b8aa63231f6..a03d97dfba1 100644 --- a/packages/fuels/test/features/dev.test.ts +++ b/packages/fuels/test/features/dev.test.ts @@ -40,7 +40,13 @@ describe('dev', () => { const { autoStartFuelCore, killChildProcess } = mockStartFuelCore(); const build = vi.spyOn(buildMod, 'build').mockReturnValue(Promise.resolve()); - const deploy = vi.spyOn(deployMod, 'deploy').mockReturnValue(Promise.resolve([])); + const deploy = vi.spyOn(deployMod, 'deploy').mockReturnValue( + Promise.resolve({ + contracts: [], + scripts: [], + predicates: [], + }) + ); // eslint-disable-next-line @typescript-eslint/no-explicit-any const on: any = vi.fn(() => ({ on })); diff --git a/packages/script/src/script.ts b/packages/script/src/script.ts index 01ccb7cad8c..765ce671e51 100644 --- a/packages/script/src/script.ts +++ b/packages/script/src/script.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Interface } from '@fuel-ts/abi-coder'; import type { InputValue, JsonAbi } from '@fuel-ts/abi-coder'; -import type { Account, Provider } from '@fuel-ts/account'; +import { deployScriptOrPredicate, type Account, type Provider } from '@fuel-ts/account'; import { FuelError } from '@fuel-ts/errors'; import { AbstractScript } from '@fuel-ts/interfaces'; import type { BytesLike } from '@fuel-ts/interfaces'; @@ -120,4 +120,23 @@ export class Script, TOutput> extends AbstractScript { return this; } + + /** + * + * @param account - The account used to pay the deployment costs. + * @returns The _blobId_ and a _waitForResult_ callback that returns the deployed predicate + * once the blob deployment transaction finishes. + * + * The returned loader script will have the same configurable constants + * as the original script which was used to generate the loader script. + */ + deploy(account: Account) { + return deployScriptOrPredicate({ + deployer: account, + abi: this.interface.jsonAbi, + bytecode: this.bytes, + loaderInstanceCallback: (loaderBytecode, newAbi) => + new Script(loaderBytecode, newAbi, this.account) as T, + }); + } } diff --git a/packages/transactions/src/coders/receipt.test.ts b/packages/transactions/src/coders/receipt.test.ts index 77b185c1957..7199104f06e 100644 --- a/packages/transactions/src/coders/receipt.test.ts +++ b/packages/transactions/src/coders/receipt.test.ts @@ -19,6 +19,7 @@ describe('ReceiptCoder', () => { it('Can encode Call', () => { const receipt: Receipt = { type: ReceiptType.Call, + id: B256, from: B256, to: B256, amount: bn(0), @@ -134,6 +135,10 @@ describe('ReceiptCoder', () => { const receipt: Receipt = { type: ReceiptType.Log, id: B256, + ra: bn(0), + rb: bn(0), + rc: bn(0), + rd: bn(0), val0: bn(0), val1: bn(0), val2: bn(0), @@ -158,6 +163,8 @@ describe('ReceiptCoder', () => { const receipt: Receipt = { type: ReceiptType.LogData, id: B256, + ra: bn(0), + rb: bn(0), val0: bn(0), val1: bn(0), ptr: bn(0), @@ -183,6 +190,7 @@ describe('ReceiptCoder', () => { it('Can encode Transfer', () => { const receipt: Receipt = { type: ReceiptType.Transfer, + id: B256, from: B256, to: B256, amount: bn(0), @@ -206,6 +214,7 @@ describe('ReceiptCoder', () => { it('Can encode TransferOut', () => { const receipt: Receipt = { type: ReceiptType.TransferOut, + id: B256, from: B256, to: B256, amount: bn(0), diff --git a/packages/transactions/src/coders/receipt.ts b/packages/transactions/src/coders/receipt.ts index f5ce228dd69..722cc1ad92c 100644 --- a/packages/transactions/src/coders/receipt.ts +++ b/packages/transactions/src/coders/receipt.ts @@ -26,8 +26,12 @@ export enum ReceiptType /* u8 */ { export type ReceiptCall = { type: ReceiptType.Call; - /** Contract ID of current context if in an internal context, zero otherwise (b256) */ + /** + * @deprecated This property is deprecated and it will be removed soon. Use property `id` instead. + */ from: string; + /** Contract ID of current context if in an internal context, zero otherwise (b256) */ + id: string; /** Contract ID of called contract (b256) */ to: string; /** Amount of coins to forward, i.e. $rB (u64) */ @@ -77,7 +81,7 @@ export class ReceiptCallCoder extends Coder { let o = offset; [decoded, o] = new B256Coder().decode(data, o); - const from = decoded; + const id = decoded; [decoded, o] = new B256Coder().decode(data, o); const to = decoded; [decoded, o] = new BigNumberCoder('u64').decode(data, o); @@ -98,7 +102,8 @@ export class ReceiptCallCoder extends Coder { return [ { type: ReceiptType.Call, - from, + id, + from: id, to, amount, assetId, @@ -377,14 +382,30 @@ export type ReceiptLog = { type: ReceiptType.Log; /** Contract ID of current context if in an internal context, zero otherwise (b256) */ id: string; - /** Value of register $rA (u64) */ + /** + * @deprecated This property is deprecated and it will be removed soon. Use property `ra` instead. + */ val0: BN; - /** Value of register $rB (u64) */ + /** Value of register $rA (u64) */ + ra: BN; + /** + * @deprecated This property is deprecated and it will be removed soon. Use property `rb` instead. + */ val1: BN; - /** Value of register $rC (u64) */ + /** Value of register $rB (u64) */ + rb: BN; + /** + * @deprecated This property is deprecated and it will be removed soon. Use property `rc` instead. + */ val2: BN; - /** Value of register $rD (u64) */ + /** Value of register $rC (u64) */ + rc: BN; + /** + * @deprecated This property is deprecated and it will be removed soon. Use property `rd` instead. + */ val3: BN; + /** Value of register $rD (u64) */ + rd: BN; /** Value of register $pc (u64) */ pc: BN; /** Value of register $is (u64) */ @@ -422,13 +443,13 @@ export class ReceiptLogCoder extends Coder { [decoded, o] = new B256Coder().decode(data, o); const id = decoded; [decoded, o] = new BigNumberCoder('u64').decode(data, o); - const val0 = decoded; + const ra = decoded; [decoded, o] = new BigNumberCoder('u64').decode(data, o); - const val1 = decoded; + const rb = decoded; [decoded, o] = new BigNumberCoder('u64').decode(data, o); - const val2 = decoded; + const rc = decoded; [decoded, o] = new BigNumberCoder('u64').decode(data, o); - const val3 = decoded; + const rd = decoded; [decoded, o] = new BigNumberCoder('u64').decode(data, o); const pc = decoded; [decoded, o] = new BigNumberCoder('u64').decode(data, o); @@ -438,10 +459,14 @@ export class ReceiptLogCoder extends Coder { { type: ReceiptType.Log, id, - val0, - val1, - val2, - val3, + ra, + rb, + rc, + rd, + val0: ra, + val1: rb, + val2: rc, + val3: rd, pc, is, }, @@ -455,8 +480,16 @@ export type ReceiptLogData = { /** Contract ID of current context if in an internal context, zero otherwise (b256) */ id: string; /** Value of register $rA (u64) */ + ra: BN; + /** + * @deprecated This property is deprecated and it will be removed soon. Use property `ra` instead. + */ val0: BN; /** Value of register $rB (u64) */ + rb: BN; + /** + * @deprecated This property is deprecated and it will be removed soon. Use property `rb` instead. + */ val1: BN; /** Value of register $rC (u64) */ ptr: BN; @@ -505,9 +538,9 @@ export class ReceiptLogDataCoder extends Coder { [decoded, o] = new B256Coder().decode(data, o); const id = decoded; [decoded, o] = new BigNumberCoder('u64').decode(data, o); - const val0 = decoded; + const ra = decoded; [decoded, o] = new BigNumberCoder('u64').decode(data, o); - const val1 = decoded; + const rb = decoded; [decoded, o] = new BigNumberCoder('u64').decode(data, o); const ptr = decoded; [decoded, o] = new BigNumberCoder('u64').decode(data, o); @@ -525,8 +558,10 @@ export class ReceiptLogDataCoder extends Coder { { type: ReceiptType.LogData, id, - val0, - val1, + ra, + rb, + val0: ra, + val1: rb, ptr, len, digest, @@ -541,8 +576,12 @@ export class ReceiptLogDataCoder extends Coder { export type ReceiptTransfer = { type: ReceiptType.Transfer; - /** Contract ID of current context if in an internal context, zero otherwise (b256) */ + /** + * @deprecated This property is deprecated and it will be removed soon. Use property `id` instead. + */ from: string; + /** Contract ID of current context if in an internal context, zero otherwise (b256) */ + id: string; /** Contract ID of contract to transfer coins to (b256) */ to: string; /** Amount of coins transferred (u64) */ @@ -583,7 +622,7 @@ export class ReceiptTransferCoder extends Coder