1
1
import type { AddressInput } from '@fuel-ts/address' ;
2
2
import { Address } from '@fuel-ts/address' ;
3
3
import { ErrorCode , FuelError } from '@fuel-ts/errors' ;
4
- import { BN , bn } from '@fuel-ts/math' ;
4
+ import type { BN } from '@fuel-ts/math' ;
5
+ import { bn } from '@fuel-ts/math' ;
5
6
import type { Transaction } from '@fuel-ts/transactions' ;
6
7
import { InputType , InputMessageCoder , TransactionCoder } from '@fuel-ts/transactions' ;
7
8
import type { BytesLike } from '@fuel-ts/utils' ;
8
9
import { arrayify , hexlify , DateTime , isDefined } from '@fuel-ts/utils' ;
9
10
import { checkFuelCoreVersionCompatibility , versions } from '@fuel-ts/versions' ;
10
- import { equalBytes } from '@noble/curves/abstract/utils' ;
11
11
import type { DocumentNode } from 'graphql' ;
12
12
import { GraphQLClient } from 'graphql-request' ;
13
13
import type { GraphQLClientResponse , GraphQLResponse } from 'graphql-request/src/types' ;
@@ -30,6 +30,7 @@ import type {
30
30
GqlRelayedTransactionFailed ,
31
31
Requester ,
32
32
GqlBlockFragment ,
33
+ GqlEstimatePredicatesQuery ,
33
34
} from './__generated__/operations' ;
34
35
import type { Coin } from './coin' ;
35
36
import type { CoinQuantity , CoinQuantityLike } from './coin-quantity' ;
@@ -47,6 +48,7 @@ import type {
47
48
ScriptTransactionRequest ,
48
49
} from './transaction-request' ;
49
50
import {
51
+ isPredicate ,
50
52
isTransactionTypeCreate ,
51
53
isTransactionTypeScript ,
52
54
transactionRequestify ,
@@ -947,48 +949,78 @@ export default class Provider {
947
949
}
948
950
949
951
/**
950
- * Verifies whether enough gas is available to complete transaction.
952
+ * Estimates the gas usage for predicates in a transaction request .
951
953
*
952
954
* @template T - The type of the transaction request object.
953
955
*
954
- * @param transactionRequest - The transaction request object .
955
- * @returns A promise that resolves to the estimated transaction request object .
956
+ * @param transactionRequest - The transaction request to estimate predicates for .
957
+ * @returns A promise that resolves to the updated transaction request with estimated gas usage for predicates .
956
958
*/
957
959
async estimatePredicates < T extends TransactionRequest > ( transactionRequest : T ) : Promise < T > {
958
- const shouldEstimatePredicates = Boolean (
959
- transactionRequest . inputs . find (
960
- ( input ) =>
961
- 'predicate' in input &&
962
- input . predicate &&
963
- ! equalBytes ( arrayify ( input . predicate ) , arrayify ( '0x' ) ) &&
964
- new BN ( input . predicateGasUsed ) . isZero ( )
965
- )
960
+ const shouldEstimatePredicates = transactionRequest . inputs . some (
961
+ ( input ) => isPredicate ( input ) && bn ( input . predicateGasUsed ) . isZero ( )
966
962
) ;
963
+
967
964
if ( ! shouldEstimatePredicates ) {
968
965
return transactionRequest ;
969
966
}
967
+
970
968
const encodedTransaction = hexlify ( transactionRequest . toTransactionBytes ( ) ) ;
969
+
971
970
const response = await this . operations . estimatePredicates ( {
972
971
encodedTransaction,
973
972
} ) ;
974
973
975
- const {
976
- estimatePredicates : { inputs } ,
977
- } = response ;
974
+ const { estimatePredicates } = response ;
978
975
979
- if ( inputs ) {
980
- inputs . forEach ( ( input , index ) => {
981
- if ( 'predicateGasUsed' in input && bn ( input . predicateGasUsed ) . gt ( 0 ) ) {
982
- // eslint-disable-next-line no-param-reassign
983
- ( < CoinTransactionRequestInput > transactionRequest . inputs [ index ] ) . predicateGasUsed =
984
- input . predicateGasUsed ;
985
- }
986
- } ) ;
987
- }
976
+ // eslint-disable-next-line no-param-reassign
977
+ transactionRequest = this . parseEstimatePredicatesResponse (
978
+ transactionRequest ,
979
+ estimatePredicates
980
+ ) ;
988
981
989
982
return transactionRequest ;
990
983
}
991
984
985
+ /**
986
+ * Estimates the gas price and predicates for a given transaction request and block horizon.
987
+ *
988
+ * @param transactionRequest - The transaction request to estimate predicates and gas price for.
989
+ * @param blockHorizon - The block horizon to use for gas price estimation.
990
+ * @returns A promise that resolves to an object containing the updated transaction
991
+ * request and the estimated gas price.
992
+ */
993
+ async estimatePredicatesAndGasPrice < T extends TransactionRequest > (
994
+ transactionRequest : T ,
995
+ blockHorizon : number
996
+ ) {
997
+ const shouldEstimatePredicates = transactionRequest . inputs . some (
998
+ ( input ) => isPredicate ( input ) && bn ( input . predicateGasUsed ) . isZero ( )
999
+ ) ;
1000
+
1001
+ if ( ! shouldEstimatePredicates ) {
1002
+ const gasPrice = await this . estimateGasPrice ( blockHorizon ) ;
1003
+
1004
+ return { transactionRequest, gasPrice } ;
1005
+ }
1006
+
1007
+ const {
1008
+ estimateGasPrice : { gasPrice } ,
1009
+ estimatePredicates,
1010
+ } = await this . operations . estimatePredicatesAndGasPrice ( {
1011
+ blockHorizon : String ( blockHorizon ) ,
1012
+ encodedTransaction : hexlify ( transactionRequest . toTransactionBytes ( ) ) ,
1013
+ } ) ;
1014
+
1015
+ // eslint-disable-next-line no-param-reassign
1016
+ transactionRequest = this . parseEstimatePredicatesResponse (
1017
+ transactionRequest ,
1018
+ estimatePredicates
1019
+ ) ;
1020
+
1021
+ return { transactionRequest, gasPrice : bn ( gasPrice ) } ;
1022
+ }
1023
+
992
1024
/**
993
1025
* Will dryRun a transaction and check for missing dependencies.
994
1026
*
@@ -1355,10 +1387,16 @@ export default class Provider {
1355
1387
addedSignatures = signedRequest . witnesses . length - lengthBefore ;
1356
1388
}
1357
1389
1358
- await this . estimatePredicates ( signedRequest ) ;
1359
- txRequestClone . updatePredicateGasUsed ( signedRequest . inputs ) ;
1390
+ let gasPrice : BN ;
1360
1391
1361
- const gasPrice = gasPriceParam ?? ( await this . estimateGasPrice ( 10 ) ) ;
1392
+ if ( gasPriceParam ) {
1393
+ gasPrice = gasPriceParam ;
1394
+ await this . estimatePredicates ( signedRequest ) ;
1395
+ } else {
1396
+ ( { gasPrice } = await this . estimatePredicatesAndGasPrice ( signedRequest , 10 ) ) ;
1397
+ }
1398
+
1399
+ txRequestClone . updatePredicateGasUsed ( signedRequest . inputs ) ;
1362
1400
1363
1401
/**
1364
1402
* Calculate minGas and maxGas based on the real transaction
@@ -2176,4 +2214,24 @@ export default class Provider {
2176
2214
statusReason : status . reason ,
2177
2215
} ) ;
2178
2216
}
2217
+
2218
+ /**
2219
+ * @hidden
2220
+ */
2221
+ private parseEstimatePredicatesResponse < T extends TransactionRequest > (
2222
+ transactionRequest : T ,
2223
+ { inputs } : GqlEstimatePredicatesQuery [ 'estimatePredicates' ]
2224
+ ) : T {
2225
+ if ( inputs ) {
2226
+ inputs . forEach ( ( input , i ) => {
2227
+ if ( input && 'predicateGasUsed' in input && bn ( input . predicateGasUsed ) . gt ( 0 ) ) {
2228
+ // eslint-disable-next-line no-param-reassign
2229
+ ( < CoinTransactionRequestInput > transactionRequest . inputs [ i ] ) . predicateGasUsed =
2230
+ input . predicateGasUsed ;
2231
+ }
2232
+ } ) ;
2233
+ }
2234
+
2235
+ return transactionRequest ;
2236
+ }
2179
2237
}
0 commit comments