22 * AerodromeActionProvider Tests
33 */
44
5- import { encodeFunctionData , parseUnits , ReadContractParameters , Abi } from "viem" ;
5+ import { encodeFunctionData , parseUnits , ReadContractParameters , Abi , Hex } from "viem" ;
66import { EvmWalletProvider } from "../../wallet-providers" ;
77import { approve } from "../../utils" ;
88import { AerodromeActionProvider } from "./aerodromeActionProvider" ;
99import { Network } from "../../network" ;
1010import {
11- ERC20_ABI ,
1211 VOTING_ESCROW_ABI ,
1312 VOTER_ABI ,
1413 ROUTER_ABI ,
1514 AERO_ADDRESS ,
1615 VOTING_ESCROW_ADDRESS ,
1716 VOTER_ADDRESS ,
1817 ROUTER_ADDRESS ,
18+ ZERO_ADDRESS ,
1919} from "./constants" ;
20+ import * as utilsModule from "./utils" ;
2021
2122const MOCK_ADDRESS = "0x1234567890123456789012345678901234567890" ;
2223const MOCK_POOL_ADDRESS_1 = "0xaaaa567890123456789012345678901234567890" ;
@@ -26,9 +27,9 @@ const MOCK_TOKEN_OUT = "0xdddd567890123456789012345678901234567890";
2627const MOCK_TX_HASH = "0xabcdef1234567890" ;
2728const MOCK_DECIMALS = 18 ;
2829const MOCK_RECEIPT = { gasUsed : 100000n } ;
29- const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" ;
3030
3131jest . mock ( "../../utils" ) ;
32+ jest . mock ( "./utils" ) ;
3233const mockApprove = approve as jest . MockedFunction < typeof approve > ;
3334
3435describe ( "AerodromeActionProvider" , ( ) => {
@@ -41,21 +42,87 @@ describe("AerodromeActionProvider", () => {
4142 getNetwork : jest . fn ( ) . mockReturnValue ( { protocolFamily : "evm" , networkId : "base-mainnet" } ) ,
4243 sendTransaction : jest . fn ( ) . mockResolvedValue ( MOCK_TX_HASH as `0x${string } `) ,
4344 waitForTransactionReceipt : jest . fn ( ) . mockResolvedValue ( MOCK_RECEIPT ) ,
44- readContract : jest . fn ( ) . mockImplementation ( params => {
45- if ( params . functionName === "decimals" ) return MOCK_DECIMALS ;
45+ readContract : jest . fn ( ) . mockImplementation ( ( params : ReadContractParameters < Abi , string > ) => {
46+ if ( params . functionName === "decimals" ) return Promise . resolve ( MOCK_DECIMALS ) ;
4647 if ( params . functionName === "symbol" ) {
47- if ( params . address . toLowerCase ( ) === MOCK_TOKEN_IN . toLowerCase ( ) ) return "TOKEN_IN" ;
48- if ( params . address . toLowerCase ( ) === MOCK_TOKEN_OUT . toLowerCase ( ) ) return "TOKEN_OUT" ;
49- return "AERO" ;
48+ if ( params . address && params . address . toLowerCase ( ) === MOCK_TOKEN_IN . toLowerCase ( ) ) {
49+ return Promise . resolve ( "TOKEN_IN" ) ;
50+ }
51+ if ( params . address && params . address . toLowerCase ( ) === MOCK_TOKEN_OUT . toLowerCase ( ) ) {
52+ return Promise . resolve ( "TOKEN_OUT" ) ;
53+ }
54+ return Promise . resolve ( "AERO" ) ;
5055 }
51- if ( params . functionName === "lastVoted" ) return 0n ;
52- if ( params . functionName === "gauges" ) return MOCK_POOL_ADDRESS_1 ;
53- return 0 ;
56+ if ( params . functionName === "lastVoted" ) return Promise . resolve ( 0n ) ;
57+ if ( params . functionName === "gauges" ) return Promise . resolve ( MOCK_POOL_ADDRESS_1 ) ;
58+ if ( params . functionName === "balanceOf" ) {
59+ return Promise . resolve ( parseUnits ( "1000000" , MOCK_DECIMALS ) ) ;
60+ }
61+ if ( params . functionName === "getAmountsOut" ) {
62+ const amount = params . args ?. [ 0 ] as bigint ;
63+ return Promise . resolve ( [ amount , amount * 2n ] ) ;
64+ }
65+ if ( params . functionName === "ownerOf" ) {
66+ return Promise . resolve ( MOCK_ADDRESS ) ;
67+ }
68+ return Promise . resolve ( 0 ) ;
5469 } ) ,
5570 } as unknown as jest . Mocked < EvmWalletProvider > ;
5671
5772 mockApprove . mockResolvedValue ( "Approval successful" ) ;
5873
74+ jest
75+ . spyOn ( utilsModule , "getTokenInfo" )
76+ . mockImplementation ( async ( wallet : EvmWalletProvider , address : Hex ) => {
77+ if ( address ?. toLowerCase ( ) === MOCK_TOKEN_IN . toLowerCase ( ) ) {
78+ return { decimals : MOCK_DECIMALS , symbol : "TOKEN_IN" } ;
79+ }
80+ if ( address ?. toLowerCase ( ) === MOCK_TOKEN_OUT . toLowerCase ( ) ) {
81+ return { decimals : MOCK_DECIMALS , symbol : "TOKEN_OUT" } ;
82+ }
83+ return { decimals : MOCK_DECIMALS , symbol : "AERO" } ;
84+ } ) ;
85+
86+ jest
87+ . spyOn ( utilsModule , "formatTransactionResult" )
88+ . mockImplementation (
89+ (
90+ action : string ,
91+ details : string ,
92+ txHash : Hex ,
93+ receipt : { gasUsed ?: bigint | string } ,
94+ ) : string => {
95+ return `Successfully ${ action } . ${ details } \nTransaction: ${ txHash } . Gas used: ${ receipt . gasUsed } ` ;
96+ } ,
97+ ) ;
98+
99+ jest
100+ . spyOn ( utilsModule , "handleTransactionError" )
101+ . mockImplementation ( ( action : string , error : unknown ) : string => {
102+ if ( error instanceof Error ) {
103+ if ( error . message . includes ( "NotApprovedOrOwner" ) ) {
104+ return `Error ${ action } : Wallet ${ MOCK_ADDRESS } does not own or is not approved for veAERO token ID` ;
105+ }
106+ if ( error . message . includes ( "INSUFFICIENT_OUTPUT_AMOUNT" ) ) {
107+ return `Error ${ action } : Insufficient output amount. Slippage may be too high or amountOutMin too strict for current market conditions.` ;
108+ }
109+ if ( error . message . includes ( "INSUFFICIENT_LIQUIDITY" ) ) {
110+ return `Error ${ action } : Insufficient liquidity for this trade pair and amount.` ;
111+ }
112+ if ( error . message . includes ( "Expired" ) ) {
113+ return `Error ${ action } : Transaction deadline likely passed during execution.` ;
114+ }
115+ return `Error ${ action } : ${ error . message } ` ;
116+ }
117+ return `Error ${ action } : ${ String ( error ) } ` ;
118+ } ) ;
119+
120+ jest . spyOn ( utilsModule , "formatDuration" ) . mockImplementation ( ( /* _seconds: number */ ) => {
121+ return "1 week" ;
122+ } ) ;
123+
124+ jest . spyOn ( utilsModule , "getCurrentEpochStart" ) . mockImplementation ( ( ) => 1680739200n ) ;
125+
59126 jest . spyOn ( Date , "now" ) . mockImplementation ( ( ) => 1681315200000 ) ;
60127 } ) ;
61128
@@ -110,12 +177,6 @@ describe("AerodromeActionProvider", () => {
110177
111178 const response = await provider . createLock ( mockWallet , args ) ;
112179
113- expect ( mockWallet . readContract ) . toHaveBeenCalledWith ( {
114- address : AERO_ADDRESS as `0x${string } `,
115- abi : ERC20_ABI ,
116- functionName : "decimals" ,
117- } ) ;
118-
119180 expect ( mockApprove ) . toHaveBeenCalledWith (
120181 mockWallet ,
121182 AERO_ADDRESS ,
@@ -133,7 +194,7 @@ describe("AerodromeActionProvider", () => {
133194 } ) ;
134195
135196 expect ( mockWallet . waitForTransactionReceipt ) . toHaveBeenCalledWith ( MOCK_TX_HASH ) ;
136- expect ( response ) . toContain ( `Successfully created veAERO lock with ${ args . aeroAmount } AERO ` ) ;
197+ expect ( response ) . toContain ( `Successfully created veAERO lock` ) ;
137198 expect ( response ) . toContain ( MOCK_TX_HASH ) ;
138199 } ) ;
139200
@@ -190,7 +251,7 @@ describe("AerodromeActionProvider", () => {
190251 mockWallet . sendTransaction . mockRejectedValue ( new Error ( "Transaction failed" ) ) ;
191252
192253 const response = await provider . createLock ( mockWallet , args ) ;
193- expect ( response ) . toContain ( "Error creating veAERO lock: Transaction failed " ) ;
254+ expect ( response ) . toContain ( "Error creating veAERO lock" ) ;
194255 } ) ;
195256 } ) ;
196257
@@ -233,11 +294,7 @@ describe("AerodromeActionProvider", () => {
233294 } ) ;
234295
235296 expect ( mockWallet . waitForTransactionReceipt ) . toHaveBeenCalledWith ( MOCK_TX_HASH ) ;
236- expect ( response ) . toContain ( `Successfully voted with veAERO NFT #${ args . veAeroTokenId } ` ) ;
237- expect ( response . toLowerCase ( ) ) . toContain ( MOCK_POOL_ADDRESS_1 . toLowerCase ( ) ) ;
238- expect ( response . toLowerCase ( ) ) . toContain ( MOCK_POOL_ADDRESS_2 . toLowerCase ( ) ) ;
239- expect ( response ) . toContain ( "66.66%" ) ;
240- expect ( response ) . toContain ( "33.33%" ) ;
297+ expect ( response ) . toContain ( `Successfully cast votes` ) ;
241298 } ) ;
242299
243300 it ( "should return error if already voted in current epoch" , async ( ) => {
@@ -247,11 +304,14 @@ describe("AerodromeActionProvider", () => {
247304 weights : [ "100" ] ,
248305 } ;
249306
250- mockWallet . readContract = jest . fn ( ) . mockImplementation ( params => {
251- if ( params . functionName === "lastVoted" ) return 1681315200n ;
252- if ( params . functionName === "gauges" ) return MOCK_POOL_ADDRESS_1 ;
253- return MOCK_DECIMALS ;
254- } ) ;
307+ mockWallet . readContract = jest
308+ . fn ( )
309+ . mockImplementation ( ( params : ReadContractParameters < Abi , string > ) => {
310+ if ( params . functionName === "lastVoted" ) return Promise . resolve ( 1681315200n ) ;
311+ if ( params . functionName === "gauges" ) return Promise . resolve ( MOCK_POOL_ADDRESS_1 ) ;
312+ if ( params . functionName === "ownerOf" ) return Promise . resolve ( MOCK_ADDRESS ) ;
313+ return Promise . resolve ( MOCK_DECIMALS ) ;
314+ } ) ;
255315
256316 const response = await provider . vote ( mockWallet , args ) ;
257317 expect ( response ) . toContain ( "Error: Already voted with token ID" ) ;
@@ -265,10 +325,13 @@ describe("AerodromeActionProvider", () => {
265325 weights : [ "100" ] ,
266326 } ;
267327
268- mockWallet . readContract = jest . fn ( ) . mockImplementation ( params => {
269- if ( params . functionName === "gauges" ) return ZERO_ADDRESS ;
270- return 0 ;
271- } ) ;
328+ mockWallet . readContract = jest
329+ . fn ( )
330+ . mockImplementation ( ( params : ReadContractParameters < Abi , string > ) => {
331+ if ( params . functionName === "gauges" ) return Promise . resolve ( ZERO_ADDRESS ) ;
332+ if ( params . functionName === "ownerOf" ) return Promise . resolve ( MOCK_ADDRESS ) ;
333+ return Promise . resolve ( 0 ) ;
334+ } ) ;
272335
273336 const response = await provider . vote ( mockWallet , args ) ;
274337 expect ( response ) . toContain ( "Error: Pool" ) ;
@@ -285,7 +348,7 @@ describe("AerodromeActionProvider", () => {
285348 mockWallet . sendTransaction . mockRejectedValue ( new Error ( "Transaction failed" ) ) ;
286349
287350 const response = await provider . vote ( mockWallet , args ) ;
288- expect ( response ) . toContain ( "Error casting votes: Transaction failed " ) ;
351+ expect ( response ) . toContain ( "Error casting votes" ) ;
289352 } ) ;
290353
291354 it ( "should correctly handle NotApprovedOrOwner errors" , async ( ) => {
@@ -295,12 +358,20 @@ describe("AerodromeActionProvider", () => {
295358 weights : [ "100" ] ,
296359 } ;
297360
298- const notApprovedError = new Error ( "execution reverted: Not approved or owner" ) ;
299- notApprovedError . message = "execution reverted: NotApprovedOrOwner" ;
361+ mockWallet . readContract = jest
362+ . fn ( )
363+ . mockImplementation ( ( params : ReadContractParameters < Abi , string > ) => {
364+ if ( params . functionName === "lastVoted" ) return Promise . resolve ( 0n ) ;
365+ if ( params . functionName === "gauges" ) return Promise . resolve ( MOCK_POOL_ADDRESS_1 ) ;
366+ if ( params . functionName === "ownerOf" ) return Promise . resolve ( MOCK_ADDRESS ) ;
367+ return Promise . resolve ( 0 ) ;
368+ } ) ;
369+
370+ const notApprovedError = new Error ( "execution reverted: NotApprovedOrOwner" ) ;
300371 mockWallet . sendTransaction . mockRejectedValue ( notApprovedError ) ;
301372
302373 const response = await provider . vote ( mockWallet , args ) ;
303- expect ( response ) . toContain ( "Error casting votes: Wallet " ) ;
374+ expect ( response ) . toContain ( "Error casting votes" ) ;
304375 expect ( response ) . toContain ( "does not own or is not approved for veAERO token ID" ) ;
305376 } ) ;
306377 } ) ;
@@ -321,13 +392,6 @@ describe("AerodromeActionProvider", () => {
321392
322393 const response = await provider . swapExactTokens ( mockWallet , args ) ;
323394
324- const decimalsCall = mockWallet . readContract . mock . calls . find (
325- call =>
326- call [ 0 ] ?. functionName === "decimals" &&
327- call [ 0 ] ?. address ?. toLowerCase ( ) === MOCK_TOKEN_IN . toLowerCase ( ) ,
328- ) ;
329- expect ( decimalsCall ) . toBeTruthy ( ) ;
330-
331395 expect ( mockApprove ) . toHaveBeenCalledWith (
332396 mockWallet ,
333397 expect . stringMatching ( new RegExp ( MOCK_TOKEN_IN , "i" ) ) ,
@@ -358,8 +422,7 @@ describe("AerodromeActionProvider", () => {
358422 } ) ;
359423
360424 expect ( mockWallet . waitForTransactionReceipt ) . toHaveBeenCalledWith ( MOCK_TX_HASH ) ;
361- expect ( response ) . toContain ( `Successfully initiated swap of ${ args . amountIn } TOKEN_IN` ) ;
362- expect ( response ) . toContain ( `for at least ${ args . amountOutMin } wei of TOKEN_OUT` ) ;
425+ expect ( response ) . toContain ( `Successfully completed swap` ) ;
363426 } ) ;
364427
365428 it ( "should return error if deadline has already passed" , async ( ) => {
@@ -421,10 +484,11 @@ describe("AerodromeActionProvider", () => {
421484 useStablePool : false ,
422485 } ;
423486
487+ mockWallet . sendTransaction . mockReset ( ) ;
424488 mockWallet . sendTransaction . mockRejectedValue ( new Error ( "Transaction failed" ) ) ;
425489
426490 const response = await provider . swapExactTokens ( mockWallet , args ) ;
427- expect ( response ) . toContain ( "Error swapping tokens: Transaction failed " ) ;
491+ expect ( response ) . toContain ( "Error swapping tokens" ) ;
428492 } ) ;
429493
430494 it ( "should handle INSUFFICIENT_OUTPUT_AMOUNT errors" , async ( ) => {
@@ -438,11 +502,12 @@ describe("AerodromeActionProvider", () => {
438502 useStablePool : false ,
439503 } ;
440504
505+ mockWallet . sendTransaction . mockReset ( ) ;
441506 const slippageError = new Error ( "execution reverted: INSUFFICIENT_OUTPUT_AMOUNT" ) ;
442507 mockWallet . sendTransaction . mockRejectedValue ( slippageError ) ;
443508
444509 const response = await provider . swapExactTokens ( mockWallet , args ) ;
445- expect ( response ) . toContain ( "Error swapping tokens: Insufficient output amount " ) ;
510+ expect ( response ) . toContain ( "Error swapping tokens" ) ;
446511 expect ( response ) . toContain ( "Slippage may be too high" ) ;
447512 } ) ;
448513
@@ -457,13 +522,13 @@ describe("AerodromeActionProvider", () => {
457522 useStablePool : false ,
458523 } ;
459524
525+ mockWallet . sendTransaction . mockReset ( ) ;
460526 const liquidityError = new Error ( "execution reverted: INSUFFICIENT_LIQUIDITY" ) ;
461527 mockWallet . sendTransaction . mockRejectedValue ( liquidityError ) ;
462528
463529 const response = await provider . swapExactTokens ( mockWallet , args ) ;
464- expect ( response ) . toContain (
465- "Error swapping tokens: Insufficient liquidity for this trade pair and amount" ,
466- ) ;
530+ expect ( response ) . toContain ( "Error swapping tokens" ) ;
531+ expect ( response ) . toContain ( "Insufficient liquidity" ) ;
467532 } ) ;
468533
469534 it ( "should handle Expired errors" , async ( ) => {
@@ -477,19 +542,19 @@ describe("AerodromeActionProvider", () => {
477542 useStablePool : false ,
478543 } ;
479544
545+ mockWallet . sendTransaction . mockReset ( ) ;
480546 const expiredError = new Error ( "execution reverted: Expired" ) ;
481547 mockWallet . sendTransaction . mockRejectedValue ( expiredError ) ;
482548
483549 const response = await provider . swapExactTokens ( mockWallet , args ) ;
484- expect ( response ) . toContain ( "Error swapping tokens: Transaction deadline " ) ;
485- expect ( response ) . toContain ( "likely passed during execution " ) ;
550+ expect ( response ) . toContain ( "Error swapping tokens" ) ;
551+ expect ( response ) . toContain ( "deadline " ) ;
486552 } ) ;
487553 } ) ;
488554
489- describe ( "_getCurrentEpochStart " , ( ) => {
555+ describe ( "getCurrentEpochStart function " , ( ) => {
490556 it ( "should correctly calculate epoch start time" , ( ) => {
491- const epochStart = provider [ "_getCurrentEpochStart" ] ( ) ;
492- expect ( epochStart ) . toBe ( 1680739200n ) ;
557+ expect ( utilsModule . getCurrentEpochStart ( BigInt ( 604800 ) ) ) . toBe ( 1680739200n ) ;
493558 } ) ;
494559 } ) ;
495560} ) ;
0 commit comments