@@ -15,12 +15,19 @@ import {
15
15
MAX_GAS_BLOCK_PERCENT ,
16
16
INTRINSIC_GAS ,
17
17
DUMMY_AUTHORIZATION_SIGNATURE ,
18
+ simulateGasBatch ,
18
19
} from './gas' ;
19
- import type { SimulationResponse } from '../api/simulation-api' ;
20
+ import type {
21
+ SimulationResponse ,
22
+ SimulationResponseTransaction ,
23
+ } from '../api/simulation-api' ;
20
24
import { simulateTransactions } from '../api/simulation-api' ;
21
25
import type { TransactionControllerMessenger } from '../TransactionController' ;
22
26
import { TransactionEnvelopeType , type TransactionMeta } from '../types' ;
23
- import type { AuthorizationList } from '../types' ;
27
+ import type {
28
+ AuthorizationList ,
29
+ TransactionBatchSingleRequest ,
30
+ } from '../types' ;
24
31
25
32
jest . mock ( '@metamask/controller-utils' , ( ) => ( {
26
33
...jest . requireActual ( '@metamask/controller-utils' ) ,
@@ -861,4 +868,146 @@ describe('gas', () => {
861
868
expect ( result ) . toBe ( toHex ( maxGasLimit ) ) ;
862
869
} ) ;
863
870
} ) ;
871
+
872
+ describe ( 'simulateGasBatch' , ( ) => {
873
+ const FROM_MOCK = '0xabc' ;
874
+ const TO_MOCK = '0xdef' ;
875
+ const VALUE_MOCK = '0x1' ;
876
+ const VALUE_MOCK_2 = '0x2' ;
877
+ const DATA_MOCK = '0xabcdef' ;
878
+ const DATA_MOCK_2 = '0x123456' ;
879
+ const GAS_MOCK_1 = '0x5208' ; // 21000 gas
880
+ const GAS_MOCK_2 = '0x7a120' ; // 500000 gas
881
+ const TRANSACTION_BATCH_REQUEST_MOCK = [
882
+ {
883
+ params : {
884
+ data : DATA_MOCK ,
885
+ to : TO_MOCK ,
886
+ value : VALUE_MOCK ,
887
+ } ,
888
+ } ,
889
+ {
890
+ params : {
891
+ data : DATA_MOCK_2 ,
892
+ to : TO_MOCK ,
893
+ value : VALUE_MOCK_2 ,
894
+ } ,
895
+ } ,
896
+ ] as TransactionBatchSingleRequest [ ] ;
897
+
898
+ const SIMULATED_TRANSACTIONS_RESPONSE_MOCK = {
899
+ transactions : [ { gasLimit : GAS_MOCK_1 } , { gasLimit : GAS_MOCK_2 } ] ,
900
+ } as unknown as SimulationResponse ;
901
+
902
+ beforeEach ( ( ) => {
903
+ jest . resetAllMocks ( ) ;
904
+ } ) ;
905
+
906
+ it ( 'returns simulated gas limits for each transaction and the total gas limit' , async ( ) => {
907
+ simulateTransactionsMock . mockResolvedValueOnce (
908
+ SIMULATED_TRANSACTIONS_RESPONSE_MOCK ,
909
+ ) ;
910
+
911
+ const result = await simulateGasBatch ( {
912
+ chainId : CHAIN_ID_MOCK ,
913
+ from : FROM_MOCK ,
914
+ transactions : TRANSACTION_BATCH_REQUEST_MOCK ,
915
+ } ) ;
916
+
917
+ expect ( result ) . toStrictEqual ( {
918
+ transactions : [
919
+ {
920
+ ...TRANSACTION_BATCH_REQUEST_MOCK [ 0 ] ,
921
+ params : {
922
+ ...TRANSACTION_BATCH_REQUEST_MOCK [ 0 ] . params ,
923
+ gas : GAS_MOCK_1 ,
924
+ } ,
925
+ } ,
926
+ {
927
+ ...TRANSACTION_BATCH_REQUEST_MOCK [ 1 ] ,
928
+ params : {
929
+ ...TRANSACTION_BATCH_REQUEST_MOCK [ 1 ] . params ,
930
+ gas : GAS_MOCK_2 ,
931
+ } ,
932
+ } ,
933
+ ] ,
934
+ gasLimit : '0x7f328' , // Total gas limit (21000 + 500000 = 521000)
935
+ } ) ;
936
+
937
+ expect ( simulateTransactionsMock ) . toHaveBeenCalledTimes ( 1 ) ;
938
+ expect ( simulateTransactionsMock ) . toHaveBeenCalledWith ( CHAIN_ID_MOCK , {
939
+ transactions : [
940
+ {
941
+ ...TRANSACTION_BATCH_REQUEST_MOCK [ 0 ] . params ,
942
+ from : FROM_MOCK ,
943
+ } ,
944
+ {
945
+ ...TRANSACTION_BATCH_REQUEST_MOCK [ 1 ] . params ,
946
+ from : FROM_MOCK ,
947
+ } ,
948
+ ] ,
949
+ } ) ;
950
+ } ) ;
951
+
952
+ it ( 'throws an error if the simulated response does not match the number of transactions' , async ( ) => {
953
+ simulateTransactionsMock . mockResolvedValueOnce ( {
954
+ transactions : [
955
+ { gasLimit : GAS_MOCK_1 } as unknown as SimulationResponseTransaction ,
956
+ ] , // Only one transaction returned
957
+ } ) ;
958
+
959
+ await expect (
960
+ simulateGasBatch ( {
961
+ chainId : CHAIN_ID_MOCK ,
962
+ from : FROM_MOCK ,
963
+ transactions : TRANSACTION_BATCH_REQUEST_MOCK ,
964
+ } ) ,
965
+ ) . rejects . toThrow (
966
+ 'Simulated gas response does not match the number of transactions' ,
967
+ ) ;
968
+
969
+ expect ( simulateTransactionsMock ) . toHaveBeenCalledTimes ( 1 ) ;
970
+ } ) ;
971
+
972
+ it ( 'throws an error if no simulated gas is returned for a transaction' , async ( ) => {
973
+ simulateTransactionsMock . mockResolvedValueOnce ( {
974
+ transactions : [
975
+ { gasLimit : undefined } ,
976
+ { gasLimit : GAS_MOCK_2 } ,
977
+ ] as unknown as SimulationResponseTransaction [ ] ,
978
+ } ) ;
979
+
980
+ await expect (
981
+ simulateGasBatch ( {
982
+ chainId : CHAIN_ID_MOCK ,
983
+ from : FROM_MOCK ,
984
+ transactions : TRANSACTION_BATCH_REQUEST_MOCK ,
985
+ } ) ,
986
+ ) . rejects . toThrow ( 'No simulated gas returned' ) ;
987
+
988
+ expect ( simulateTransactionsMock ) . toHaveBeenCalledTimes ( 1 ) ;
989
+ } ) ;
990
+
991
+ it ( 'handles empty transactions gracefully' , async ( ) => {
992
+ simulateTransactionsMock . mockResolvedValueOnce ( {
993
+ transactions : [ ] ,
994
+ } ) ;
995
+
996
+ const result = await simulateGasBatch ( {
997
+ chainId : CHAIN_ID_MOCK ,
998
+ from : FROM_MOCK ,
999
+ transactions : [ ] ,
1000
+ } ) ;
1001
+
1002
+ expect ( result ) . toStrictEqual ( {
1003
+ transactions : [ ] ,
1004
+ gasLimit : '0x0' , // Total gas limit is 0
1005
+ } ) ;
1006
+
1007
+ expect ( simulateTransactionsMock ) . toHaveBeenCalledTimes ( 1 ) ;
1008
+ expect ( simulateTransactionsMock ) . toHaveBeenCalledWith ( CHAIN_ID_MOCK , {
1009
+ transactions : [ ] ,
1010
+ } ) ;
1011
+ } ) ;
1012
+ } ) ;
864
1013
} ) ;
0 commit comments