@@ -2,13 +2,18 @@ import type { TypedTransaction } from '@ethereumjs/tx';
22import { TransactionFactory } from '@ethereumjs/tx' ;
33import { Contract } from '@ethersproject/contracts' ;
44import type { Provider } from '@metamask/network-controller' ;
5- import type { Hex } from '@metamask/utils' ;
5+ import { add0x , type Hex } from '@metamask/utils' ;
6+ import BN from 'bn.js' ;
67
78import { OracleLayer1GasFeeFlow } from './OracleLayer1GasFeeFlow' ;
89import { CHAIN_IDS } from '../constants' ;
910import type { TransactionControllerMessenger } from '../TransactionController' ;
10- import type { Layer1GasFeeFlowRequest , TransactionMeta } from '../types' ;
11- import { TransactionStatus } from '../types' ;
11+ import {
12+ type Layer1GasFeeFlowRequest ,
13+ type TransactionMeta ,
14+ TransactionStatus ,
15+ } from '../types' ;
16+ import { bnFromHex , padHexToEvenLength } from '../utils/utils' ;
1217
1318jest . mock ( '@ethersproject/contracts' , ( ) => ( {
1419 Contract : jest . fn ( ) ,
@@ -36,7 +41,8 @@ const TRANSACTION_META_MOCK: TransactionMeta = {
3641
3742const SERIALIZED_TRANSACTION_MOCK = '0x1234' ;
3843const ORACLE_ADDRESS_MOCK = '0x5678' as Hex ;
39- const LAYER_1_FEE_MOCK = '0x9ABCD' ;
44+ const LAYER_1_FEE_MOCK = '0x09abcd' ;
45+ const OPERATOR_FEE_MOCK = '0x5' ;
4046const DEFAULT_GAS_PRICE_ORACLE_ADDRESS =
4147 '0x420000000000000000000000000000000000000F' ;
4248
@@ -98,9 +104,9 @@ class DefaultOracleLayer1GasFeeFlow extends OracleLayer1GasFeeFlow {
98104
99105describe ( 'OracleLayer1GasFeeFlow' , ( ) => {
100106 const contractMock = jest . mocked ( Contract ) ;
101- const contractGetL1FeeMock : jest . MockedFn <
102- ( ) => Promise < { toHexString : ( ) => string } >
103- > = jest . fn ( ) ;
107+ const contractGetL1FeeMock : jest . MockedFn < ( ) => Promise < BN > > = jest . fn ( ) ;
108+ const contractGetOperatorFeeMock : jest . MockedFn < ( ) => Promise < BN > > =
109+ jest . fn ( ) ;
104110
105111 let request : Layer1GasFeeFlowRequest ;
106112
@@ -112,13 +118,14 @@ describe('OracleLayer1GasFeeFlow', () => {
112118
113119 contractMock . mockClear ( ) ;
114120 contractGetL1FeeMock . mockClear ( ) ;
121+ contractGetOperatorFeeMock . mockClear ( ) ;
115122
116- contractGetL1FeeMock . mockResolvedValue ( {
117- toHexString : ( ) => LAYER_1_FEE_MOCK ,
118- } ) ;
123+ contractGetL1FeeMock . mockResolvedValue ( bnFromHex ( LAYER_1_FEE_MOCK ) ) ;
124+ contractGetOperatorFeeMock . mockResolvedValue ( new BN ( 0 ) ) ;
119125
120126 contractMock . mockReturnValue ( {
121127 getL1Fee : contractGetL1FeeMock ,
128+ getOperatorFee : contractGetOperatorFeeMock ,
122129 } as unknown as Contract ) ;
123130 } ) ;
124131
@@ -156,6 +163,7 @@ describe('OracleLayer1GasFeeFlow', () => {
156163 expect ( contractGetL1FeeMock ) . toHaveBeenCalledWith (
157164 serializedTransactionMock ,
158165 ) ;
166+ expect ( contractGetOperatorFeeMock ) . not . toHaveBeenCalled ( ) ;
159167 } ) ;
160168
161169 it ( 'signs transaction with dummy key if supported by flow' , async ( ) => {
@@ -180,6 +188,7 @@ describe('OracleLayer1GasFeeFlow', () => {
180188 } ) ;
181189
182190 expect ( typedTransactionMock . sign ) . toHaveBeenCalledTimes ( 1 ) ;
191+ expect ( contractGetOperatorFeeMock ) . not . toHaveBeenCalled ( ) ;
183192 } ) ;
184193
185194 describe ( 'throws' , ( ) => {
@@ -228,5 +237,79 @@ describe('OracleLayer1GasFeeFlow', () => {
228237 expect ( oracleAddress ) . toBe ( DEFAULT_GAS_PRICE_ORACLE_ADDRESS ) ;
229238 expect ( typedTransactionMock . sign ) . not . toHaveBeenCalled ( ) ;
230239 } ) ;
240+
241+ it ( 'adds operator fee when gas used is available' , async ( ) => {
242+ const gasUsed = '0x5208' ;
243+ request = {
244+ ...request ,
245+ transactionMeta : {
246+ ...request . transactionMeta ,
247+ gasUsed,
248+ } ,
249+ } ;
250+
251+ contractGetOperatorFeeMock . mockResolvedValueOnce (
252+ bnFromHex ( OPERATOR_FEE_MOCK ) ,
253+ ) ;
254+
255+ const flow = new MockOracleLayer1GasFeeFlow ( false ) ;
256+ const response = await flow . getLayer1Fee ( request ) ;
257+
258+ expect ( contractGetOperatorFeeMock ) . toHaveBeenCalledTimes ( 1 ) ;
259+ expect ( contractGetOperatorFeeMock ) . toHaveBeenCalledWith ( gasUsed ) ;
260+ expect ( response ) . toStrictEqual ( {
261+ layer1Fee : add0x (
262+ padHexToEvenLength (
263+ bnFromHex ( LAYER_1_FEE_MOCK )
264+ . add ( bnFromHex ( OPERATOR_FEE_MOCK ) )
265+ . toString ( 16 ) ,
266+ ) ,
267+ ) as Hex ,
268+ } ) ;
269+ } ) ;
270+
271+ it ( 'defaults operator fee to zero when call fails' , async ( ) => {
272+ const gasUsed = '0x1' ;
273+ request = {
274+ ...request ,
275+ transactionMeta : {
276+ ...request . transactionMeta ,
277+ gasUsed,
278+ } ,
279+ } ;
280+
281+ contractGetOperatorFeeMock . mockRejectedValueOnce ( new Error ( 'revert' ) ) ;
282+
283+ const flow = new MockOracleLayer1GasFeeFlow ( false ) ;
284+ const response = await flow . getLayer1Fee ( request ) ;
285+
286+ expect ( contractGetOperatorFeeMock ) . toHaveBeenCalledTimes ( 1 ) ;
287+ expect ( response ) . toStrictEqual ( {
288+ layer1Fee : LAYER_1_FEE_MOCK ,
289+ } ) ;
290+ } ) ;
291+
292+ it ( 'defaults operator fee to zero when call returns undefined' , async ( ) => {
293+ const gasUsed = '0x2' ;
294+ request = {
295+ ...request ,
296+ transactionMeta : {
297+ ...request . transactionMeta ,
298+ gasUsed,
299+ } ,
300+ } ;
301+
302+ contractGetOperatorFeeMock . mockResolvedValueOnce (
303+ undefined as unknown as BN ,
304+ ) ;
305+
306+ const flow = new MockOracleLayer1GasFeeFlow ( false ) ;
307+ const response = await flow . getLayer1Fee ( request ) ;
308+
309+ expect ( contractGetOperatorFeeMock ) . toHaveBeenCalledTimes ( 1 ) ;
310+ expect ( response ) . toStrictEqual ( {
311+ layer1Fee : LAYER_1_FEE_MOCK ,
312+ } ) ;
313+ } ) ;
231314 } ) ;
232315} ) ;
0 commit comments