Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added support for unknown txn types #3003

Merged
merged 21 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9a18682
feat: added support for unknown txn types
maschad Aug 22, 2024
7495302
docs: add changeset
maschad Aug 22, 2024
5dcb8a8
Merge branch 'master' into mc/chore/handle-unknown-txns
maschad Aug 22, 2024
dd5cb42
fix: ensure base transaction type is distinguished from unsupported
maschad Aug 22, 2024
9d3efb6
fix: update property check
maschad Aug 22, 2024
dba5a70
Merge branch 'master' into mc/chore/handle-unknown-txns
maschad Aug 22, 2024
552ca6c
test: remove unnecessary only
maschad Aug 22, 2024
86c00fd
Merge branch 'master' into mc/chore/handle-unknown-txns
maschad Aug 23, 2024
6b88425
fix: remove transaction encoding and decoding + hide UnknowTransactio…
maschad Aug 23, 2024
28849bf
fix: update transaction code
maschad Aug 26, 2024
13c8db6
Merge branch 'master' into mc/chore/handle-unknown-txns
maschad Aug 26, 2024
3189053
Merge branch 'master' into mc/chore/handle-unknown-txns
maschad Aug 27, 2024
606bae5
fix: ensure retrieval of unknown txns return a warning
maschad Aug 27, 2024
ac7201c
Merge branch 'master' into mc/chore/handle-unknown-txns
maschad Aug 27, 2024
e7daa05
chore: clean up message and throw warning for single case
maschad Aug 27, 2024
14500a8
chore: remove unknown transaction type
maschad Aug 29, 2024
75453ec
Merge branch 'master' into mc/chore/handle-unknown-txns
maschad Aug 29, 2024
af4f051
chore: remove other unused types
maschad Aug 29, 2024
347ff9b
linting
maschad Aug 29, 2024
c136a24
Merge branch 'master' into mc/chore/handle-unknown-txns
maschad Aug 30, 2024
398ae1f
Merge branch 'master' into mc/chore/handle-unknown-txns
maschad Aug 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/honest-toes-pump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@fuel-ts/transactions": patch
"@fuel-ts/account": patch
---

feat: added support for unknown txn types
6 changes: 5 additions & 1 deletion packages/account/src/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import type {
ExcludeResourcesOption,
Provider,
ScriptTransactionRequestLike,
TransactionResponse,
TransactionCost,
EstimateTransactionParams,
CursorPaginationArgs,
Expand All @@ -36,6 +35,8 @@ import {
ScriptTransactionRequest,
transactionRequestify,
addAmountToCoinQuantities,
isTransactionTypeUnknown,
TransactionResponse,
} from './providers';
import {
cacheRequestInputsResourcesFromOwner,
Expand Down Expand Up @@ -632,6 +633,9 @@ export class Account extends AbstractAccount {
);
}
const transactionRequest = transactionRequestify(transactionRequestLike);
if (isTransactionTypeUnknown(transactionRequest)) {
maschad marked this conversation as resolved.
Show resolved Hide resolved
return new TransactionResponse(transactionRequest, this.provider);
}
if (estimateTxDependencies) {
await this.provider.estimateTxDependencies(transactionRequest);
}
Expand Down
6 changes: 5 additions & 1 deletion packages/account/src/predicate/predicate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
transactionRequestify,
isRequestInputResource,
isRequestInputResourceFromOwner,
isTransactionTypeUnknown,
TransactionResponse,
} from '../providers';
import type {
CallResult,
Expand All @@ -20,7 +22,6 @@ import type {
Resource,
TransactionRequest,
TransactionRequestLike,
TransactionResponse,
} from '../providers';

import { getPredicateRoot } from './utils';
Expand Down Expand Up @@ -117,6 +118,9 @@ export class Predicate<
*/
sendTransaction(transactionRequestLike: TransactionRequestLike): Promise<TransactionResponse> {
const transactionRequest = transactionRequestify(transactionRequestLike);
if (isTransactionTypeUnknown(transactionRequest)) {
maschad marked this conversation as resolved.
Show resolved Hide resolved
return Promise.resolve(new TransactionResponse(transactionRequest, this.provider));
}
return super.sendTransaction(transactionRequest, { estimateTxDependencies: false });
}

Expand Down
81 changes: 81 additions & 0 deletions packages/account/src/providers/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import {
MESSAGE_PROOF_RAW_RESPONSE,
MESSAGE_PROOF,
} from '../../test/fixtures';
import {
MOCK_TX_UNKNOWN_RAW_PAYLOAD,
MOCK_TX_SCRIPT_RAW_PAYLOAD,
} from '../../test/fixtures/transaction-summary';
import { setupTestProviderAndWallets, launchNode, TestMessage } from '../test-utils';

import type { Coin } from './coin';
Expand Down Expand Up @@ -56,6 +60,83 @@ const getCustomFetch =
* @group node
*/
describe('Provider', () => {
it('should throw an error when retrieving a transaction with an unknown transaction type', async () => {
using launched = await setupTestProviderAndWallets();
const { provider } = launched;

const mockProvider = await Provider.create(provider.url, {
fetch: getCustomFetch('getTransaction', {
transaction: {
id: '0x1234567890abcdef',
rawPayload: MOCK_TX_UNKNOWN_RAW_PAYLOAD, // Unknown transaction type
},
}),
});

// Spy on console.warn
const consoleWarnSpy = vi.spyOn(console, 'warn');

// Verify that only one transaction was returned (the known type)
const transaction = await mockProvider.getTransaction('0x1234567890abcdef');

expect(transaction).toBeNull();

expect(consoleWarnSpy).toHaveBeenCalledWith(
expect.stringContaining('Unsupported transaction type encountered')
);

// Clean up
consoleWarnSpy.mockRestore();
});

it('should log a warning when retrieving batch transactions with an unknown transaction type', async () => {
using launched = await setupTestProviderAndWallets();
const { provider: nodeProvider } = launched;

// Create a mock provider with custom getTransactions operation
const mockProvider = await Provider.create(nodeProvider.url, {
fetch: getCustomFetch('getTransactions', {
transactions: {
edges: [
{
node: {
id: '0x1234567890abcdef',
rawPayload: MOCK_TX_UNKNOWN_RAW_PAYLOAD,
},
},
{
node: {
id: '0xabcdef1234567890',
rawPayload: MOCK_TX_SCRIPT_RAW_PAYLOAD,
},
},
],
pageInfo: {
hasNextPage: false,
hasPreviousPage: false,
startCursor: null,
endCursor: null,
},
},
}),
});

// Spy on console.warn
const consoleWarnSpy = vi.spyOn(console, 'warn');

// Verify that only one transaction was returned (the known type)
const { transactions } = await mockProvider.getTransactions();
expect(transactions.length).toBe(1);

// Check if warning was logged
expect(consoleWarnSpy).toHaveBeenCalledWith(
expect.stringContaining('Unsupported transaction type encountered')
);

// Clean up
consoleWarnSpy.mockRestore();
});

it('can getVersion()', async () => {
using launched = await setupTestProviderAndWallets();
const { provider } = launched;
Expand Down
42 changes: 34 additions & 8 deletions packages/account/src/providers/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import type {
import {
isTransactionTypeCreate,
isTransactionTypeScript,
isTransactionTypeUnknown,
transactionRequestify,
} from './transaction-request';
import type { TransactionResultReceipt } from './transaction-response';
Expand Down Expand Up @@ -739,12 +740,15 @@ Supported fuel-core version: ${supportedVersion}.`
* @param sendTransactionParams - The provider send transaction parameters (optional).
* @returns A promise that resolves to the transaction response object.
*/
// #region Provider-sendTransaction
async sendTransaction(
transactionRequestLike: TransactionRequestLike,
{ estimateTxDependencies = true }: ProviderSendTxParams = {}
): Promise<TransactionResponse> {
const transactionRequest = transactionRequestify(transactionRequestLike);
if (isTransactionTypeUnknown(transactionRequest)) {
return new TransactionResponse(transactionRequest, this);
}
// #region Provider-sendTransaction
maschad marked this conversation as resolved.
Show resolved Hide resolved
Torres-ssf marked this conversation as resolved.
Show resolved Hide resolved
if (estimateTxDependencies) {
await this.estimateTxDependencies(transactionRequest);
}
Expand Down Expand Up @@ -1434,13 +1438,24 @@ Supported fuel-core version: ${supportedVersion}.`
transactionId: string
): Promise<Transaction<TTransactionType> | null> {
const { transaction } = await this.operations.getTransaction({ transactionId });

if (!transaction) {
return null;
}
return new TransactionCoder().decode(
arrayify(transaction.rawPayload),
0
)?.[0] as Transaction<TTransactionType>;

try {
return new TransactionCoder().decode(
arrayify(transaction.rawPayload),
0
)?.[0] as Transaction<TTransactionType>;
} catch (error) {
if (error instanceof FuelError && error.code === ErrorCode.UNSUPPORTED_TRANSACTION_TYPE) {
// eslint-disable-next-line no-console
console.warn('Unsupported transaction type encountered');
return null;
}
throw error;
}
}

/**
Expand All @@ -1454,9 +1469,20 @@ Supported fuel-core version: ${supportedVersion}.`
} = await this.operations.getTransactions(paginationArgs);

const coder = new TransactionCoder();
const transactions = edges.map(
({ node: { rawPayload } }) => coder.decode(arrayify(rawPayload), 0)[0]
);
const transactions = edges
.map(({ node: { rawPayload } }) => {
try {
return coder.decode(arrayify(rawPayload), 0)[0];
} catch (error) {
if (error instanceof FuelError && error.code === ErrorCode.UNSUPPORTED_TRANSACTION_TYPE) {
// eslint-disable-next-line no-console
console.warn('Unsupported transaction type encountered');
return null;
}
throw error;
}
})
.filter((tx): tx is Transaction => tx !== null);

return { transactions, pageInfo };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './transaction-request';
export * from './blob-transaction-request';
export * from './create-transaction-request';
export * from './script-transaction-request';
export * from './unknown-transaction-request';
export * from './errors';
export * from './scripts';
export * from './types';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
Policy,
TransactionCreate,
TransactionBlob,
TransactionUnknown,
} from '@fuel-ts/transactions';
import {
PolicyType,
Expand Down Expand Up @@ -192,7 +193,11 @@ export abstract class BaseTransactionRequest implements BaseTransactionRequestLi
};
}

abstract toTransaction(): TransactionCreate | TransactionScript | TransactionBlob;
abstract toTransaction():
| TransactionCreate
| TransactionScript
| TransactionBlob
| TransactionUnknown;

/**
* Converts the transaction request to a byte array.
Expand Down
7 changes: 5 additions & 2 deletions packages/account/src/providers/transaction-request/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,18 @@ import type {
ScriptTransactionRequest,
ScriptTransactionRequestLike,
} from './script-transaction-request';
import type { UnknownTransactionRequest } from './unknown-transaction-request';

export type TransactionRequest =
| ScriptTransactionRequest
| CreateTransactionRequest
| BlobTransactionRequest;
| BlobTransactionRequest
| UnknownTransactionRequest;
export type TransactionRequestLike =
| ({ type: TransactionType.Script } & ScriptTransactionRequestLike)
| ({ type: TransactionType.Create } & CreateTransactionRequestLike)
| ({ type: TransactionType.Blob } & BlobTransactionRequestLike);
| ({ type: TransactionType.Blob } & BlobTransactionRequestLike)
| ({ type: TransactionType.Unknown } & UnknownTransactionRequest);

export type JsonAbisFromAllCalls = {
main: JsonAbi;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { BytesLike } from '@fuel-ts/interfaces';
import type { TransactionUnknown } from '@fuel-ts/transactions';

import type { BaseTransactionRequestLike } from './transaction-request';
import { BaseTransactionRequest, TransactionType } from './transaction-request';

/**
* @hidden
*/
export interface UnknownTransactionRequestLike extends BaseTransactionRequestLike {
bytes?: BytesLike;
}
/**
* @hidden
*/
export class UnknownTransactionRequest extends BaseTransactionRequest {
maschad marked this conversation as resolved.
Show resolved Hide resolved
static from(obj: UnknownTransactionRequestLike) {
if (obj instanceof this) {
return obj;
}
return new this(obj);
}

/** Type of the transaction */
type = TransactionType.Unknown as const;

/** Data of the transaction */
bytes?: BytesLike;

constructor({ bytes, ...rest }: UnknownTransactionRequestLike = {}) {
super(rest);
this.bytes = bytes;
}

/**
* Gets the Transaction Request by hashing the transaction.
*
* @param chainId - The chain ID.
*
* @returns - A hash of the transaction, which is the transaction ID.
*/
getTransactionId(): string {
return '';
}

/**
* Converts the transaction request to a `TransactionBlob`.
*
* @returns The transaction create object.
*/
toTransaction(): TransactionUnknown {
return {
...this.getBaseTransaction(),
type: TransactionType.Unknown,
bytes: this.bytes,
};
}
}
20 changes: 19 additions & 1 deletion packages/account/src/providers/transaction-request/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { BlobTransactionRequest } from './blob-transaction-request';
import { CreateTransactionRequest } from './create-transaction-request';
import { ScriptTransactionRequest } from './script-transaction-request';
import { isTransactionTypeBlob, isTransactionTypeCreate, isTransactionTypeScript } from './utils';
import { UnknownTransactionRequest } from './unknown-transaction-request';
import {
isTransactionTypeBlob,
isTransactionTypeCreate,
isTransactionTypeScript,
isTransactionTypeUnknown,
} from './utils';

/**
* @group node
Expand Down Expand Up @@ -42,3 +48,15 @@ describe('isTransactionTypeBlob', () => {
expect(isTransactionTypeCreate(request)).toBe(false);
});
});

describe('isTransactionTypeUnknown', () => {
it('should return true if the request is an unknown transaction', () => {
const request = new UnknownTransactionRequest({ bytes: '0x' });
expect(isTransactionTypeUnknown(request)).toBe(true);
});

it('should return false if the request is not an unknown transaction', () => {
const request = new ScriptTransactionRequest();
expect(isTransactionTypeUnknown(request)).toBe(false);
});
});
Loading