-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
transaction-response.ts
153 lines (139 loc) · 4.86 KB
/
transaction-response.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { arrayify } from '@ethersproject/bytes';
import type {
ReceiptCall,
ReceiptLog,
ReceiptLogData,
ReceiptPanic,
ReceiptReturn,
ReceiptReturnData,
ReceiptRevert,
ReceiptTransfer,
ReceiptTransferOut,
ReceiptScriptResult,
} from '@fuel-ts/transactions';
import { ReceiptType, ReceiptCoder } from '@fuel-ts/transactions';
import type {
GqlGetTransactionWithReceiptsQuery,
GqlReceiptFragmentFragment,
} from '../__generated__/operations';
import type Provider from '../provider';
import type { TransactionRequest } from '../transaction-request';
import { getGasUsedFromReceipts } from '../util';
export type TransactionResultCallReceipt = ReceiptCall;
export type TransactionResultReturnReceipt = ReceiptReturn;
export type TransactionResultReturnDataReceipt = ReceiptReturnData & { data: string };
export type TransactionResultPanicReceipt = ReceiptPanic;
export type TransactionResultRevertReceipt = ReceiptRevert;
export type TransactionResultLogReceipt = ReceiptLog;
export type TransactionResultLogDataReceipt = ReceiptLogData & { data: string };
export type TransactionResultTransferReceipt = ReceiptTransfer;
export type TransactionResultTransferOutReceipt = ReceiptTransferOut;
export type TransactionResultScriptResultReceipt = ReceiptScriptResult;
export type TransactionResultReceipt =
| TransactionResultCallReceipt
| TransactionResultReturnReceipt
| TransactionResultReturnDataReceipt
| TransactionResultPanicReceipt
| TransactionResultRevertReceipt
| TransactionResultLogReceipt
| TransactionResultLogDataReceipt
| TransactionResultTransferReceipt
| TransactionResultTransferOutReceipt
| TransactionResultScriptResultReceipt;
export type TransactionResult<TStatus extends 'success' | 'failure'> = {
status: TStatus extends 'success'
? { type: 'success'; programState: any }
: { type: 'failure'; reason: any };
/** Receipts produced during the execution of the transaction */
receipts: TransactionResultReceipt[];
transactionId: string;
blockId: any;
time: any;
};
const processGqlReceipt = (gqlReceipt: GqlReceiptFragmentFragment): TransactionResultReceipt => {
const receipt = new ReceiptCoder().decode(arrayify(gqlReceipt.rawPayload), 0)[0];
switch (receipt.type) {
case ReceiptType.ReturnData: {
return {
...receipt,
data: gqlReceipt.data!,
};
}
case ReceiptType.LogData: {
return {
...receipt,
data: gqlReceipt.data!,
};
}
default:
return receipt;
}
};
export class TransactionResponse {
/** Transaction ID */
id: string;
/** Transaction request */
request: TransactionRequest;
provider: Provider;
/** Gas used on the transaction */
gasUsed: bigint = 0n;
constructor(id: string, request: TransactionRequest, provider: Provider) {
this.id = id;
this.request = request;
this.provider = provider;
}
async #fetch(): Promise<NonNullable<GqlGetTransactionWithReceiptsQuery['transaction']>> {
const { transaction } = await this.provider.operations.getTransactionWithReceipts({
transactionId: this.id,
});
if (!transaction) {
throw new Error('No Transaction was received from the client.');
}
return transaction;
}
/** Waits for transaction to succeed or fail and returns the result */
async waitForResult(): Promise<TransactionResult<any>> {
const transaction = await this.#fetch();
switch (transaction.status?.type) {
case 'SubmittedStatus': {
// TODO: Implement polling or GQL subscription
throw new Error('Not yet implemented');
}
case 'FailureStatus': {
const receipts = transaction.receipts!.map(processGqlReceipt);
this.gasUsed = getGasUsedFromReceipts(receipts);
return {
status: { type: 'failure', reason: transaction.status.reason },
receipts,
transactionId: this.id,
blockId: transaction.status.block.id,
time: transaction.status.time,
};
}
case 'SuccessStatus': {
const receipts = transaction.receipts!.map(processGqlReceipt);
this.gasUsed = getGasUsedFromReceipts(receipts);
return {
status: { type: 'success', programState: transaction.status.programState },
receipts,
transactionId: this.id,
blockId: transaction.status.block.id,
time: transaction.status.time,
};
}
default: {
throw new Error('Invalid Transaction status');
}
}
}
/** Waits for transaction to succeed and returns the result */
async wait(): Promise<TransactionResult<'success'>> {
const result = await this.waitForResult();
if (result.status.type === 'failure') {
throw new Error(`Transaction failed: ${result.status.reason}`);
}
return result;
}
}