Skip to content

Commit

Permalink
feat: use submitAndAwaitStatus to submit transactions (#3101)
Browse files Browse the repository at this point in the history
  • Loading branch information
nedsalk authored Sep 5, 2024
1 parent 2caf1b1 commit 309b8d5
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 18 deletions.
5 changes: 5 additions & 0 deletions .changeset/famous-pans-press.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fuel-ts/account": patch
---

feat: use `submitAndAwaitStatus` to submit transactions
17 changes: 12 additions & 5 deletions packages/account/src/providers/fuel-graphql-subscriber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,16 @@ export class FuelGraphqlSubscriber implements AsyncIterator<unknown> {
});

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return new FuelGraphqlSubscriber(response.body!.getReader());
const [errorReader, resultReader] = response.body!.tee().map((stream) => stream.getReader());

/**
* If the node threw an error, read it and throw it to the user
* Else just discard the response and return the subscriber below,
* which will have that same response via `resultReader`
*/
await new FuelGraphqlSubscriber(errorReader).next();

return new FuelGraphqlSubscriber(resultReader);
}

private events: Array<{ data: unknown; errors?: { message: string }[] }> = [];
Expand Down Expand Up @@ -94,10 +103,8 @@ export class FuelGraphqlSubscriber implements AsyncIterator<unknown> {
/**
* Gets called when `break` is called in a `for-await-of` loop.
*/
async return(): Promise<IteratorResult<unknown, undefined>> {
await this.stream.cancel();
this.stream.releaseLock();
return { done: true, value: undefined };
return(): Promise<IteratorResult<unknown, undefined>> {
return Promise.resolve({ done: true, value: undefined });
}

[Symbol.asyncIterator](): AsyncIterator<unknown, unknown, undefined> {
Expand Down
6 changes: 6 additions & 0 deletions packages/account/src/providers/operations.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,12 @@ subscription submitAndAwait($encodedTransaction: HexString!) {
}
}

subscription submitAndAwaitStatus($encodedTransaction: HexString!) {
submitAndAwaitStatus(tx: $encodedTransaction) {
...transactionStatusSubscriptionFragment
}
}

subscription statusChange($transactionId: TransactionId!) {
statusChange(id: $transactionId) {
...transactionStatusSubscriptionFragment
Expand Down
26 changes: 20 additions & 6 deletions packages/account/src/providers/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,13 +381,26 @@ type NodeInfoCache = Record<string, NodeInfo>;

type Operations = ReturnType<typeof getOperationsSdk>;

type SdkOperations = Omit<Operations, 'submitAndAwait' | 'statusChange'> & {
type SdkOperations = Omit<
Operations,
'submitAndAwait' | 'statusChange' | 'submitAndAwaitStatus'
> & {
/**
* This method is DEPRECATED and will be REMOVED in v1.
*
* This method will hang until the transaction is fully processed, as described in https://github.com/FuelLabs/fuel-core/issues/2108.
*
* Please use the `submitAndAwaitStatus` method instead.
*/
submitAndAwait: (
...args: Parameters<Operations['submitAndAwait']>
) => Promise<ReturnType<Operations['submitAndAwait']>>;
statusChange: (
...args: Parameters<Operations['statusChange']>
) => Promise<ReturnType<Operations['statusChange']>>;
submitAndAwaitStatus: (
...args: Parameters<Operations['submitAndAwaitStatus']>
) => Promise<ReturnType<Operations['submitAndAwaitStatus']>>;
getBlobs: (variables: { blobIds: string[] }) => Promise<{ blob: { id: string } | null }[]>;
};

Expand Down Expand Up @@ -825,13 +838,14 @@ Supported fuel-core version: ${supportedVersion}.`
if (isTransactionTypeScript(transactionRequest)) {
abis = transactionRequest.abis;
}
const subscription = await this.operations.submitAndAwaitStatus({ encodedTransaction });

const {
submit: { id: transactionId },
} = await this.operations.submit({ encodedTransaction });
this.#cacheInputs(transactionRequest.inputs, transactionId);
this.#cacheInputs(
transactionRequest.inputs,
transactionRequest.getTransactionId(this.getChainId())
);

return new TransactionResponse(transactionRequest, this, abis);
return new TransactionResponse(transactionRequest, this, abis, subscription);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { arrayify, assertUnreachable } from '@fuel-ts/utils';
import type {
GqlMalleableTransactionFieldsFragment,
GqlStatusChangeSubscription,
GqlSubmitAndAwaitStatusSubscription,
} from '../__generated__/operations';
import type Provider from '../provider';
import type { JsonAbisFromAllCalls, TransactionRequest } from '../transaction-request';
Expand Down Expand Up @@ -141,7 +142,12 @@ export class TransactionResponse {
* @param tx - The transaction ID or TransactionRequest.
* @param provider - The provider.
*/
constructor(tx: string | TransactionRequest, provider: Provider, abis?: JsonAbisFromAllCalls) {
constructor(
tx: string | TransactionRequest,
provider: Provider,
abis?: JsonAbisFromAllCalls,
private submitTxSubscription?: AsyncIterable<GqlSubmitAndAwaitStatusSubscription>
) {
this.id = typeof tx === 'string' ? tx : tx.getTransactionId(provider.getChainId());

this.provider = provider;
Expand Down Expand Up @@ -319,11 +325,14 @@ export class TransactionResponse {
return;
}

const subscription = await this.provider.operations.statusChange({
transactionId: this.id,
});
const subscription =
this.submitTxSubscription ??
(await this.provider.operations.statusChange({
transactionId: this.id,
}));

for await (const { statusChange } of subscription) {
for await (const sub of subscription) {
const statusChange = 'statusChange' in sub ? sub.statusChange : sub.submitAndAwaitStatus;
this.status = statusChange;
if (statusChange.type === 'SqueezedOutStatus') {
this.unsetResourceCache();
Expand Down
28 changes: 26 additions & 2 deletions packages/fuel-gauge/src/edge-cases.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { TransactionResponse, Wallet } from 'fuels';
import { launchTestNode } from 'fuels/test-utils';
import { ErrorCode, FuelError, hexlify, TransactionResponse, Wallet, WalletUnlocked } from 'fuels';
import { expectToThrowFuelError, launchTestNode } from 'fuels/test-utils';

import { PredicateFalse } from '../test/typegen';
import { CollisionInFnNamesFactory } from '../test/typegen/contracts';

import { launchTestContract } from './utils';
Expand Down Expand Up @@ -53,4 +54,27 @@ describe('Edge Cases', () => {
// we leave this intentionally empty so that we test that the subscription will end the loop when the connection is closed
}
});

test('Submitting a failing transaction via submitAndAwaitStatus subscription throws immediately', async () => {
using launched = await launchTestNode();
const {
wallets: [funder],
} = launched;

const predicate = new PredicateFalse({ provider: launched.provider });
await funder.transfer(predicate.address, 1_000_000);

const transferTx = await predicate.createTransfer(WalletUnlocked.generate().address, 1);

await expectToThrowFuelError(
() =>
launched.provider.operations.submitAndAwaitStatus({
encodedTransaction: hexlify(transferTx.toTransactionBytes()),
}),
new FuelError(
ErrorCode.INVALID_REQUEST,
'Invalid transaction data: PredicateVerificationFailed(Panic(PredicateReturnedNonOne))'
)
);
});
});

0 comments on commit 309b8d5

Please sign in to comment.