From fa83fcd0c90ddb95bc397ab2675a5ad759b94f82 Mon Sep 17 00:00:00 2001 From: Cameron Manavian Date: Mon, 26 Sep 2022 18:27:26 -0700 Subject: [PATCH] feat: add message support (#515) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Basics * Test initial state messages * clean up, use later fuel-core * Fix data decoding * Update test * changeset * rm * revert * PR code review improvments * revert sha * relocate * Update packages/wallet/src/wallet.ts Co-authored-by: Luiz Estácio | stacio.eth * Update packages/contract/src/__test__/coverage-contract/coverage-contract.test.ts Co-authored-by: Luiz Estácio | stacio.eth * lint post suggestion Co-authored-by: Luiz Estácio | stacio.eth --- .changeset/strong-hotels-whisper.md | 8 + .../coverage-contract.test.ts | 118 +++++++++++++- .../__test__/coverage-contract/src/main.sw | 2 +- packages/providers/src/index.ts | 1 + packages/providers/src/message.ts | 14 ++ packages/providers/src/operations.graphql | 31 ++++ packages/providers/src/provider.ts | 30 ++++ .../src/transaction-request/input.ts | 50 +++++- .../transaction-request.ts | 26 +++ packages/transactions/package.json | 1 + .../transactions/src/coders/input.test.ts | 49 +++++- packages/transactions/src/coders/input.ts | 151 +++++++++++++++++- packages/wallet/src/wallet.ts | 30 ++++ pnpm-lock.yaml | 2 + services/fuel-core/Dockerfile | 4 +- services/fuel-core/chainConfig.json | 23 ++- 16 files changed, 526 insertions(+), 14 deletions(-) create mode 100644 .changeset/strong-hotels-whisper.md create mode 100644 packages/providers/src/message.ts diff --git a/.changeset/strong-hotels-whisper.md b/.changeset/strong-hotels-whisper.md new file mode 100644 index 00000000000..ebba386e6bf --- /dev/null +++ b/.changeset/strong-hotels-whisper.md @@ -0,0 +1,8 @@ +--- +"@fuel-ts/contract": patch +"@fuel-ts/providers": patch +"@fuel-ts/transactions": patch +"@fuel-ts/wallet": patch +--- + +Added message support diff --git a/packages/contract/src/__test__/coverage-contract/coverage-contract.test.ts b/packages/contract/src/__test__/coverage-contract/coverage-contract.test.ts index fd7f44d1e09..5eae632322f 100644 --- a/packages/contract/src/__test__/coverage-contract/coverage-contract.test.ts +++ b/packages/contract/src/__test__/coverage-contract/coverage-contract.test.ts @@ -1,8 +1,9 @@ import { NativeAssetId } from '@fuel-ts/constants'; import type { BN } from '@fuel-ts/math'; import { bn, toHex } from '@fuel-ts/math'; -import { Provider } from '@fuel-ts/providers'; -import { TestUtils } from '@fuel-ts/wallet'; +import { Provider, ScriptTransactionRequest } from '@fuel-ts/providers'; +import type { Message } from '@fuel-ts/providers'; +import { Wallet, TestUtils } from '@fuel-ts/wallet'; import { readFileSync } from 'fs'; import { join } from 'path'; @@ -346,4 +347,117 @@ describe('Coverage Contract', () => { }; expect(unhexed).toStrictEqual(last); }); + + it('should get initial state messages from node', async () => { + const provider = new Provider('http://127.0.0.1:4000/graphql'); + + const WALLET_A = new Wallet( + '0x1ff16505df75735a5bcf4cb4cf839903120c181dd9be6781b82cda23543bd242', + provider + ); + const WALLET_B = new Wallet( + '0x30bb0bc68f5d2ec3b523cee5a65503031b40679d9c72280cd8088c2cfbc34e38', + provider + ); + + const EXPECTED_MESSAGES_A: Message[] = [ + { + amount: bn(1), + sender: WALLET_B.address, + recipient: WALLET_A.address, + data: [8, 7, 6, 5, 4], + nonce: bn(1), + daHeight: bn(0), + }, + ]; + const EXPECTED_MESSAGES_B: Message[] = [ + { + amount: bn('12704439083013451934'), + sender: WALLET_A.address, + recipient: WALLET_B.address, + data: [7], + nonce: bn('1017517292834129547'), + daHeight: bn('3684546456337077810'), + }, + ]; + + const aMessages = await WALLET_A.getMessages(); + const bMessages = await WALLET_B.getMessages(); + + expect(aMessages).toStrictEqual(EXPECTED_MESSAGES_A); + expect(bMessages).toStrictEqual(EXPECTED_MESSAGES_B); + }); + + it('should test sending input messages [1]', async () => { + const provider = new Provider('http://127.0.0.1:4000/graphql'); + const request = new ScriptTransactionRequest({ gasLimit: 1000000 }); + + const sender = await TestUtils.generateTestWallet(provider, [[1_000, NativeAssetId]]); + const receiver = await TestUtils.generateTestWallet(provider); + + const messages: Message[] = [ + { + amount: bn(900), + sender: sender.address, + recipient: receiver.address, + data: [12, 13, 14], + nonce: bn(823), + daHeight: bn(0), + }, + ]; + + request.addMessages(messages); + const response = await sender.sendTransaction(request); + + await response.wait(); + const receiverMessages = await receiver.getMessages(); + + expect(receiverMessages).toStrictEqual(messages); + }); + + it('should test sending input messages [3]', async () => { + const provider = new Provider('http://127.0.0.1:4000/graphql'); + const request = new ScriptTransactionRequest({ gasLimit: 1000000 }); + + const sender = await TestUtils.generateTestWallet(provider, [[1_000, NativeAssetId]]); + const receiver = await TestUtils.generateTestWallet(provider); + + const messages: Message[] = [ + { + amount: bn(111), + sender: sender.address, + recipient: receiver.address, + data: [11, 11, 11], + nonce: bn(100), + daHeight: bn(0), + }, + { + amount: bn(222), + sender: sender.address, + recipient: receiver.address, + data: [22, 22, 22], + nonce: bn(200), + daHeight: bn(0), + }, + { + amount: bn(333), + sender: sender.address, + recipient: receiver.address, + data: [33, 33, 33], + nonce: bn(300), + daHeight: bn(0), + }, + ]; + + request.addMessages(messages); + const response = await sender.sendTransaction(request); + + await response.wait(); + const receiverMessages = await receiver.getMessages(); + + // sort by nonce, messages are not guaranteed in order + receiverMessages.sort((a, b) => a.nonce.toNumber() - b.nonce.toNumber()); + + expect(receiverMessages).toStrictEqual(messages); + }); }); diff --git a/packages/contract/src/__test__/coverage-contract/src/main.sw b/packages/contract/src/__test__/coverage-contract/src/main.sw index c367e3ea072..414ce9cb3cf 100644 --- a/packages/contract/src/__test__/coverage-contract/src/main.sw +++ b/packages/contract/src/__test__/coverage-contract/src/main.sw @@ -161,7 +161,7 @@ impl CoverageContract for Contract { }, } } - + fn echo_u8(input: u8) -> u8 { input } diff --git a/packages/providers/src/index.ts b/packages/providers/src/index.ts index 053dd3e6acc..935f25b3866 100644 --- a/packages/providers/src/index.ts +++ b/packages/providers/src/index.ts @@ -3,6 +3,7 @@ export * from './coin-quantity'; export * from './coin'; export * from './provider'; +export * from './message'; export { default as Provider } from './provider'; export * from './transaction-request'; export * from './transaction-response'; diff --git a/packages/providers/src/message.ts b/packages/providers/src/message.ts new file mode 100644 index 00000000000..13861678845 --- /dev/null +++ b/packages/providers/src/message.ts @@ -0,0 +1,14 @@ +import type { AbstractAddress } from '@fuel-ts/interfaces'; +import type { BN } from '@fuel-ts/math'; + +/** + * A Fuel message + */ +export type Message = { + amount: BN; + sender: AbstractAddress; + recipient: AbstractAddress; + data: number[]; + daHeight: BN; + nonce: BN; +}; diff --git a/packages/providers/src/operations.graphql b/packages/providers/src/operations.graphql index c621f907f58..b2424fbd938 100644 --- a/packages/providers/src/operations.graphql +++ b/packages/providers/src/operations.graphql @@ -57,6 +57,15 @@ fragment coinFragment on Coin { blockCreated } +fragment messageFragment on Message { + amount + sender + recipient + data + nonce + daHeight +} + fragment balanceFragment on Balance { owner amount @@ -254,6 +263,28 @@ query getBalances( } } +query getMessages( + $owner: Address! + $after: String + $before: String + $first: Int + $last: Int +) { + messages( + owner: $owner + after: $after + before: $before + first: $first + last: $last + ) { + edges { + node { + ...messageFragment + } + } + } +} + mutation dryRun($encodedTransaction: HexString!, $utxoValidation: Boolean) { dryRun(tx: $encodedTransaction, utxoValidation: $utxoValidation) { ...receiptFragment diff --git a/packages/providers/src/provider.ts b/packages/providers/src/provider.ts index 69ad5416018..a8b225ece88 100644 --- a/packages/providers/src/provider.ts +++ b/packages/providers/src/provider.ts @@ -4,12 +4,14 @@ 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 { max, bn, multiply } from '@fuel-ts/math'; import type { Transaction } from '@fuel-ts/transactions'; import { + InputMessageCoder, GAS_PRICE_FACTOR, MAX_GAS_PER_TX, ReceiptType, @@ -28,6 +30,7 @@ import { getSdk as getOperationsSdk } from './__generated__/operations'; import type { Coin } from './coin'; import type { CoinQuantity, CoinQuantityLike } from './coin-quantity'; import { coinQuantityfy } from './coin-quantity'; +import type { Message } from './message'; import { ScriptTransactionRequest, transactionRequestify } from './transaction-request'; import type { TransactionRequestLike } from './transaction-request'; import type { @@ -507,6 +510,33 @@ export default class Provider { })); } + /** + * Returns message for the given address + */ + async getMessages( + /** The address to get message from */ + address: AbstractAddress, + /** Pagination arguments */ + paginationArgs?: CursorPaginationArgs + ): Promise { + const result = await this.operations.getMessages({ + first: 10, + ...paginationArgs, + owner: address.toB256(), + }); + + const messages = result.messages.edges!.map((edge) => edge!.node!); + + return messages.map((message) => ({ + amount: bn(message.amount), + sender: Address.fromAddressOrString(message.sender), + recipient: Address.fromAddressOrString(message.recipient), + data: InputMessageCoder.decodeData(message.data), + daHeight: bn(message.daHeight), + nonce: bn(message.nonce), + })); + } + async buildSpendPredicate( predicate: AbstractPredicate, amountToSpend: BigNumberish, diff --git a/packages/providers/src/transaction-request/input.ts b/packages/providers/src/transaction-request/input.ts index 2e94ddbef7a..a73f2051fee 100644 --- a/packages/providers/src/transaction-request/input.ts +++ b/packages/providers/src/transaction-request/input.ts @@ -36,6 +36,33 @@ export type CoinTransactionRequestInput = { /** Predicate input data (parameters) */ predicateData?: BytesLike; }; +export type MessageTransactionRequestInput = { + type: InputType.Message; + + /** Amount of coins */ + amount: BigNumberish; + + /** Address of sender */ + sender: BytesLike; + + /** Address of sender */ + recipient: BytesLike; + + /** Index of witness that authorizes the message */ + witnessIndex: number; + + /** data of message */ + data: number[]; + + /** Unique nonce of message */ + nonce: BigNumberish; + + /** Predicate bytecode */ + predicate?: BytesLike; + + /** Predicate input data (parameters) */ + predicateData?: BytesLike; +}; export type ContractTransactionRequestInput = { type: InputType.Contract; @@ -45,7 +72,10 @@ export type ContractTransactionRequestInput = { /** Contract ID */ contractId: BytesLike; }; -export type TransactionRequestInput = CoinTransactionRequestInput | ContractTransactionRequestInput; +export type TransactionRequestInput = + | CoinTransactionRequestInput + | ContractTransactionRequestInput + | MessageTransactionRequestInput; export const inputify = (value: TransactionRequestInput): Input => { switch (value.type) { @@ -89,6 +119,24 @@ export const inputify = (value: TransactionRequestInput): Input => { contractID: hexlify(value.contractId), }; } + case InputType.Message: { + const predicate = arrayify(value.predicate ?? '0x'); + const predicateData = arrayify(value.predicateData ?? '0x'); + return { + type: InputType.Message, + sender: hexlify(value.sender), + recipient: hexlify(value.recipient), + amount: bn(value.amount), + nonce: bn(value.nonce), + witnessIndex: value.witnessIndex, + dataLength: value.data.length, + predicateLength: predicate.length, + predicateDataLength: predicateData.length, + data: value.data, + predicate: hexlify(predicate), + predicateData: hexlify(predicateData), + }; + } default: { throw new Error('Invalid Input type'); } diff --git a/packages/providers/src/transaction-request/transaction-request.ts b/packages/providers/src/transaction-request/transaction-request.ts index 5a24f5a4df3..06bd03993b4 100644 --- a/packages/providers/src/transaction-request/transaction-request.ts +++ b/packages/providers/src/transaction-request/transaction-request.ts @@ -23,6 +23,7 @@ import { import type { Coin } from '../coin'; import type { CoinQuantity, CoinQuantityLike } from '../coin-quantity'; import { coinQuantityfy } from '../coin-quantity'; +import type { Message } from '../message'; import { calculatePriceWithFactor } from '../util'; import type { @@ -328,6 +329,31 @@ abstract class BaseTransactionRequest implements BaseTransactionRequestLike { amount: gasFee.isZero() ? bn(1) : gasFee, }; } + + /** + * Converts the given Message to a MessageInput + */ + addMessage(message: Message) { + let witnessIndex = this.getCoinInputWitnessIndexByOwner(message.recipient); + + // Insert a dummy witness if no witness exists + if (typeof witnessIndex !== 'number') { + witnessIndex = this.createWitness(); + } + + // Insert the MessageInput + this.pushInput({ + type: InputType.Message, + ...message, + sender: message.sender.toBytes(), + recipient: message.recipient.toBytes(), + witnessIndex, + }); + } + + addMessages(messages: ReadonlyArray) { + messages.forEach((message) => this.addMessage(message)); + } } export interface ScriptTransactionRequestLike extends BaseTransactionRequestLike { diff --git a/packages/transactions/package.json b/packages/transactions/package.json index 3b25a52cd61..b3e8327586f 100644 --- a/packages/transactions/package.json +++ b/packages/transactions/package.json @@ -23,6 +23,7 @@ "license": "Apache-2.0", "dependencies": { "@ethersproject/bytes": "^5.4.0", + "@ethersproject/sha2": "^5.5.0", "@fuel-ts/abi-coder": "workspace:*", "@fuel-ts/constants": "workspace:*", "@fuel-ts/math": "workspace:*" diff --git a/packages/transactions/src/coders/input.test.ts b/packages/transactions/src/coders/input.test.ts index d4f6a33b2e3..95b7abd5d23 100644 --- a/packages/transactions/src/coders/input.test.ts +++ b/packages/transactions/src/coders/input.test.ts @@ -1,8 +1,8 @@ import { arrayify, hexlify } from '@ethersproject/bytes'; import { bn } from '@fuel-ts/math'; -import type { Input } from './input'; -import { InputCoder, InputType } from './input'; +import type { Input, InputMessage } from './input'; +import { InputMessageCoder, InputCoder, InputType } from './input'; const B256 = '0xd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'; @@ -62,4 +62,49 @@ describe('InputCoder', () => { expect(offset).toEqual((encoded.length - 2) / 2); expect(decoded).toEqual(input); }); + + it('Can encode Message Id', () => { + const input: InputMessage = { + type: InputType.Message, + amount: bn(1000), + sender: '0xf1e92c42b90934aa6372e30bc568a326f6e66a1a0288595e6e3fbd392a4f3e6e', + recipient: '0xef86afa9696cf0dc6385e2c407a6e159a1103cefb7e2ae0636fb33d3cb2a9e4a', + data: [12, 13, 14], + nonce: bn(1234), + witnessIndex: 0, + dataLength: 3, + predicateLength: 0, + predicateDataLength: 0, + predicate: '0x', + predicateData: '0x', + }; + + const EXPECTED = '0xc2675161a5856e25fe3b526f65461030d2909694582b2c39d2396b409148d6ff'; + const result = InputMessageCoder.getMessageId(input); + + expect(result).toEqual(EXPECTED); + }); + + it('Can encode Message', () => { + const input: Input = { + type: InputType.Message, + amount: bn(1000), + sender: '0xf1e92c42b90934aa6372e30bc568a326f6e66a1a0288595e6e3fbd392a4f3e6e', + recipient: '0xef86afa9696cf0dc6385e2c407a6e159a1103cefb7e2ae0636fb33d3cb2a9e4a', + data: [12, 13, 14], + nonce: bn(1234), + witnessIndex: 0, + dataLength: 3, + predicateLength: 0, + predicateDataLength: 0, + predicate: '0x', + predicateData: '0x', + }; + + const encoded = hexlify(new InputCoder().encode(input)); + + expect(encoded).toEqual( + '0x0000000000000002c2675161a5856e25fe3b526f65461030d2909694582b2c39d2396b409148d6fff1e92c42b90934aa6372e30bc568a326f6e66a1a0288595e6e3fbd392a4f3e6eef86afa9696cf0dc6385e2c407a6e159a1103cefb7e2ae0636fb33d3cb2a9e4a00000000000003e800000000000004d20000000000000000000000000000001800000000000000000000000000000000000000000000000c000000000000000d000000000000000e' + ); + }); }); diff --git a/packages/transactions/src/coders/input.ts b/packages/transactions/src/coders/input.ts index 19a63631612..fdeb08f99df 100644 --- a/packages/transactions/src/coders/input.ts +++ b/packages/transactions/src/coders/input.ts @@ -1,7 +1,7 @@ /* eslint-disable max-classes-per-file */ - -import { concat } from '@ethersproject/bytes'; -import { Coder, U64Coder, B256Coder, NumberCoder } from '@fuel-ts/abi-coder'; +import { arrayify, concat } from '@ethersproject/bytes'; +import { sha256 } from '@ethersproject/sha2'; +import { Coder, U64Coder, B256Coder, NumberCoder, ArrayCoder, WORD_SIZE } from '@fuel-ts/abi-coder'; import type { BN } from '@fuel-ts/math'; import { ByteArrayCoder } from './byte-array'; @@ -10,9 +10,10 @@ import { TxPointerCoder } from './tx-pointer'; import type { UtxoId } from './utxo-id'; import { UtxoIdCoder } from './utxo-id'; -export enum InputType /* u8 */ { +export enum InputType { Coin = 0, Contract = 1, + Message = 2, } export type InputCoin = { @@ -191,7 +192,139 @@ export class InputContractCoder extends Coder { } } -export type Input = InputCoin | InputContract; +export type InputMessage = { + type: InputType.Message; + + /** Amount of coins */ + amount: BN; + + /** Address of sender */ + sender: string; + + /** Address of sender */ + recipient: string; + + /** data of message */ + data: number[]; + + /** Unique nonce of message */ + nonce: BN; + + /** Index of witness that authorizes message (u8) */ + witnessIndex: number; + + /** Length of predicate, in instructions (u16) */ + dataLength: number; + + /** Length of predicate, in instructions (u16) */ + predicateLength: number; + + /** Length of predicate input data, in bytes (u16) */ + predicateDataLength: number; + + /** Predicate bytecode (byte[]) */ + predicate: string; + + /** Predicate input data (parameters) (byte[]) */ + predicateData: string; +}; + +export class InputMessageCoder extends Coder { + constructor() { + super('InputMessage', 'struct InputMessage', 0); + } + + static getMessageId(value: InputMessage): string { + const parts: Uint8Array[] = []; + + parts.push(new ByteArrayCoder(32).encode(value.sender)); + parts.push(new ByteArrayCoder(32).encode(value.recipient)); + parts.push(new U64Coder().encode(value.nonce)); + parts.push(new U64Coder().encode(value.amount)); + parts.push(new Uint8Array(value.data)); + return sha256(concat(parts)); + } + + encode(value: InputMessage): Uint8Array { + const parts: Uint8Array[] = []; + + const encodedData = new ArrayCoder(new NumberCoder('u8'), value.dataLength).encode(value.data); + + const mId = InputMessageCoder.getMessageId(value); + parts.push(new ByteArrayCoder(32).encode(mId)); + parts.push(new ByteArrayCoder(32).encode(value.sender)); + parts.push(new ByteArrayCoder(32).encode(value.recipient)); + parts.push(new U64Coder().encode(value.amount)); + parts.push(new U64Coder().encode(value.nonce)); + parts.push(new NumberCoder('u8').encode(value.witnessIndex)); + parts.push(new NumberCoder('u16').encode(encodedData.length)); + parts.push(new NumberCoder('u16').encode(value.predicateLength)); + parts.push(new NumberCoder('u16').encode(value.predicateDataLength)); + parts.push(encodedData); + parts.push(new ByteArrayCoder(value.predicateLength).encode(value.predicate)); + parts.push(new ByteArrayCoder(value.predicateDataLength).encode(value.predicateData)); + + return concat(parts); + } + + static decodeData(messageData: number[]): number[] { + const dataLength = messageData.length; + const [data] = new ArrayCoder(new NumberCoder('u8'), dataLength / WORD_SIZE).decode( + arrayify(messageData), + 0 + ); + + return data; + } + + decode(data: Uint8Array, offset: number): [InputMessage, number] { + let decoded; + let o = offset; + + [decoded, o] = new B256Coder().decode(data, o); + const sender = decoded; + [decoded, o] = new B256Coder().decode(data, o); + const recipient = decoded; + [decoded, o] = new U64Coder().decode(data, o); + const amount = decoded; + [decoded, o] = new U64Coder().decode(data, o); + const nonce = decoded; + [decoded, o] = new NumberCoder('u8').decode(data, o); + const witnessIndex = Number(decoded); + [decoded, o] = new NumberCoder('u16').decode(data, o); + const dataLength = decoded; + [decoded, o] = new NumberCoder('u16').decode(data, o); + const predicateLength = decoded; + [decoded, o] = new NumberCoder('u16').decode(data, o); + const predicateDataLength = decoded; + [decoded, o] = new ArrayCoder(new NumberCoder('u8'), dataLength).decode(data, o); + const messageData = decoded; + [decoded, o] = new ByteArrayCoder(predicateLength).decode(data, o); + const predicate = decoded; + [decoded, o] = new ByteArrayCoder(predicateDataLength).decode(data, o); + const predicateData = decoded; + + return [ + { + type: InputType.Message, + sender, + recipient, + amount, + witnessIndex, + nonce, + data: messageData, + dataLength, + predicateLength, + predicateDataLength, + predicate, + predicateData, + }, + o, + ]; + } +} + +export type Input = InputCoin | InputContract | InputMessage; export class InputCoder extends Coder { constructor() { @@ -211,6 +344,10 @@ export class InputCoder extends Coder { parts.push(new InputContractCoder().encode(value)); break; } + case InputType.Message: { + parts.push(new InputMessageCoder().encode(value)); + break; + } default: { throw new Error('Invalid Input type'); } @@ -234,6 +371,10 @@ export class InputCoder extends Coder { [decoded, o] = new InputContractCoder().decode(data, o); return [decoded, o]; } + case InputType.Message: { + [decoded, o] = new InputMessageCoder().decode(data, o); + return [decoded, o]; + } default: { throw new Error('Invalid Input type'); } diff --git a/packages/wallet/src/wallet.ts b/packages/wallet/src/wallet.ts index 4a3c14a49a9..97b1a07eaa4 100644 --- a/packages/wallet/src/wallet.ts +++ b/packages/wallet/src/wallet.ts @@ -18,6 +18,7 @@ import type { CallResult, BuildPredicateOptions, TransactionResult, + Message, } from '@fuel-ts/providers'; import { Signer } from '@fuel-ts/signer'; import { MAX_GAS_PER_TX } from '@fuel-ts/transactions'; @@ -147,6 +148,35 @@ export default class Wallet extends AbstractWallet { return coins; } + /** + * Gets messages owned by the wallet address. + */ + async getMessages(): Promise { + const messages = []; + + const pageSize = 9999; + let cursor; + // eslint-disable-next-line no-unreachable-loop + for (;;) { + const pageMessages = await this.provider.getMessages(this.address, { + first: pageSize, + after: cursor, + }); + + messages.push(...pageMessages); + + const hasNextPage = pageMessages.length >= pageSize; + if (!hasNextPage) { + break; + } + + // TODO: implement pagination + throw new Error(`Wallets with more than ${pageSize} messages are not yet supported`); + } + + return messages; + } + /** * Gets balance for the given asset. */ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 79c5ca0c87e..ed9c906b4cc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -529,11 +529,13 @@ importers: packages/transactions: specifiers: '@ethersproject/bytes': ^5.4.0 + '@ethersproject/sha2': ^5.5.0 '@fuel-ts/abi-coder': workspace:* '@fuel-ts/constants': workspace:* '@fuel-ts/math': workspace:* dependencies: '@ethersproject/bytes': 5.7.0 + '@ethersproject/sha2': 5.7.0 '@fuel-ts/abi-coder': link:../abi-coder '@fuel-ts/constants': link:../constants '@fuel-ts/math': link:../math diff --git a/services/fuel-core/Dockerfile b/services/fuel-core/Dockerfile index 9aae1ac525c..a1502ec1408 100644 --- a/services/fuel-core/Dockerfile +++ b/services/fuel-core/Dockerfile @@ -1,4 +1,5 @@ -FROM ghcr.io/fuellabs/fuel-core:v0.10.1 +# v0.10.1 +plus +FROM ghcr.io/fuellabs/fuel-core:sha-7b34fa8 ARG IP=0.0.0.0 ARG PORT=4000 @@ -21,7 +22,6 @@ CMD exec ./fuel-core run \ --ip ${IP} \ --port ${PORT} \ --db-path ${DB_PATH} \ - --utxo-validation \ --min-gas-price ${MIN_GAS_PRICE} \ --vm-backtrace \ --predicates \ diff --git a/services/fuel-core/chainConfig.json b/services/fuel-core/chainConfig.json index 37d18ee0b62..1694208d2d6 100644 --- a/services/fuel-core/chainConfig.json +++ b/services/fuel-core/chainConfig.json @@ -246,6 +246,26 @@ "amount": "0xFFFFFFFFFFFFFFFF", "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" } + ], + "messages": [ + { + "sender": "0xc43454aa38dd91f88109a4b7aef5efb96ce34e3f24992fe0f81d233ca686f80f", + "recipient": "0x69a2b736b60159b43bb8a4f98c0589f6da5fa3a3d101e8e269c499eb942753ba", + "owner": "0x69a2b736b60159b43bb8a4f98c0589f6da5fa3a3d101e8e269c499eb942753ba", + "nonce": "0x01", + "amount": "0x01", + "data": "0x00000000000000080000000000000007000000000000000600000000000000050000000000000004", + "da_height": "0x00" + }, + { + "sender": "0x69a2b736b60159b43bb8a4f98c0589f6da5fa3a3d101e8e269c499eb942753ba", + "recipient": "0xc43454aa38dd91f88109a4b7aef5efb96ce34e3f24992fe0f81d233ca686f80f", + "owner": "0xc43454aa38dd91f88109a4b7aef5efb96ce34e3f24992fe0f81d233ca686f80f", + "nonce": "0x0e1ef2963832068b", + "amount": "0xb04f3c08f59b309e", + "data": "0x0000000000000007", + "da_height": "0x3322237b00655632" + } ] }, "transaction_parameters": { @@ -260,6 +280,7 @@ "max_storage_slots": 255, "max_predicate_length": 1048576, "max_predicate_data_length": 1048576, - "gas_price_factor": 1000000 + "gas_price_factor": 1000000, + "max_message_data_length": 1048576 } }