@@ -20,80 +20,140 @@ import {
2020 fundErc20Address ,
2121} from '@aave/client/testing' ;
2222import { sendTransaction , signSwapTypedDataWith } from '@aave/client/viem' ;
23+ import type { Account , Chain , Transport , WalletClient } from 'viem' ;
2324import { beforeAll , describe , expect , it } from 'vitest' ;
2425
25- const user = await createNewWallet (
26- '0x7e97068be691cce1b5c1216b8bc4600fa9c605fcef07c8ef5af05f86e838d69b' ,
27- ) ;
26+ describe ( 'Token swapping on Aave V4' , ( ) => {
27+ describe ( 'Given a user who has previously swapped the current token' , ( ) => {
28+ let userDidSwap : WalletClient < Transport , Chain , Account > ;
2829
29- describe ( 'Aave V4 Swap Scenarios' , ( ) => {
30- describe ( 'Given a user wants to swap an ERC20 asset' , ( ) => {
31- describe ( 'When the user swaps an ERC20 asset' , ( ) => {
32- beforeAll ( async ( ) => {
33- const setup = await fundErc20Address ( evmAddress ( user . account . address ) , {
30+ beforeAll ( async ( ) => {
31+ userDidSwap = await createNewWallet (
32+ '0x7e97068be691cce1b5c1216b8bc4600fa9c605fcef07c8ef5af05f86e838d69b' ,
33+ ) ;
34+
35+ const setup = await fundErc20Address (
36+ evmAddress ( userDidSwap . account . address ) ,
37+ {
3438 address : ETHEREUM_USDC_ADDRESS ,
3539 amount : bigDecimal ( '20' ) ,
3640 decimals : 6 ,
41+ } ,
42+ ) ;
43+ assertOk ( setup ) ;
44+ } ) ;
45+
46+ describe ( 'When the user swaps the token again' , ( ) => {
47+ it ( 'Then the swap executes without requiring approval' , async ( {
48+ annotate,
49+ } ) => {
50+ const swapResult = await prepareTokenSwap ( client , {
51+ market : {
52+ amount : bigDecimal ( '20' ) ,
53+ sell : { erc20 : ETHEREUM_USDC_ADDRESS } ,
54+ buy : { erc20 : ETHEREUM_WETH_ADDRESS } ,
55+ chainId : ETHEREUM_FORK_ID ,
56+ kind : SwapKind . Sell ,
57+ receiver : evmAddress ( userDidSwap . account . address ) ,
58+ user : evmAddress ( userDidSwap . account . address ) ,
59+ } ,
60+ } ) . andThen ( ( swapPlan ) => {
61+ invariant (
62+ swapPlan . __typename === 'SwapByIntent' ,
63+ `Swap plan is not a swap by intent: ${ swapPlan . __typename } ` ,
64+ ) ;
65+ return signSwapTypedDataWith ( userDidSwap , swapPlan . data ) . andThen (
66+ ( signature ) => {
67+ return swap ( client , {
68+ intent : {
69+ quoteId : swapPlan . quote . quoteId ,
70+ signature : signature ,
71+ } ,
72+ } ) ;
73+ } ,
74+ ) ;
3775 } ) ;
38- assertOk ( setup ) ;
76+
77+ assertOk ( swapResult ) ;
78+ invariant (
79+ swapResult . value . __typename === 'SwapReceipt' ,
80+ `Swap result is not a swap receipt: ${ swapResult . value . __typename } ` ,
81+ ) ;
82+ annotate ( `Swap id: ${ swapResult . value . id } ` ) ;
83+ const status = await swapStatus ( client , { id : swapResult . value . id } ) ;
84+ assertOk ( status ) ;
85+ // Check swap was opened successfully
86+ expect ( status . value . __typename ) . toBe ( 'SwapOpen' ) ;
87+
88+ const swapPositions = await userSwaps ( client , {
89+ chainId : ETHEREUM_FORK_ID ,
90+ user : evmAddress ( userDidSwap . account . address ) ,
91+ } ) ;
92+ assertOk ( swapPositions ) ;
93+ expect (
94+ swapPositions . value . items . find (
95+ ( swap ) =>
96+ swap . __typename === 'SwapOpen' &&
97+ swapResult . value . __typename === 'SwapReceipt' &&
98+ swap . swapId === swapResult . value . id ,
99+ ) ,
100+ ) . toBeDefined ( ) ;
39101 } ) ;
102+ } ) ;
103+ } ) ;
40104
41- it ( `Then the user's swap positions are updated` , async ( { annotate } ) => {
105+ describe ( 'Given a user swapping a token for the first time' , ( ) => {
106+ let newUser : WalletClient < Transport , Chain , Account > ;
107+
108+ beforeAll ( async ( ) => {
109+ newUser = await createNewWallet ( ) ;
110+
111+ const setup = await fundErc20Address (
112+ evmAddress ( newUser . account . address ) ,
113+ {
114+ address : ETHEREUM_USDC_ADDRESS ,
115+ amount : bigDecimal ( '20' ) ,
116+ decimals : 6 ,
117+ } ,
118+ ) ;
119+ assertOk ( setup ) ;
120+ } ) ;
121+
122+ describe ( 'When the user initiates the swap' , ( ) => {
123+ it ( 'Then the user must approve before swapping' , async ( { annotate } ) => {
42124 const swapResult = await prepareTokenSwap ( client , {
43125 market : {
44126 amount : bigDecimal ( '20' ) ,
45127 sell : { erc20 : ETHEREUM_USDC_ADDRESS } ,
46128 buy : { erc20 : ETHEREUM_WETH_ADDRESS } ,
47129 chainId : ETHEREUM_FORK_ID ,
48130 kind : SwapKind . Sell ,
49- receiver : evmAddress ( user . account . address ) ,
50- user : evmAddress ( user . account . address ) ,
131+ receiver : evmAddress ( newUser . account . address ) ,
132+ user : evmAddress ( newUser . account . address ) ,
51133 } ,
52134 } ) . andThen ( ( swapPlan ) => {
53- switch ( swapPlan . __typename ) {
54- case 'SwapByIntent' :
55- return signSwapTypedDataWith ( user , swapPlan . data ) . andThen (
56- ( signature ) => {
57- return swap ( client , {
58- intent : {
59- quoteId : swapPlan . quote . quoteId ,
60- signature : signature ,
61- } ,
62- } ) ;
63- } ,
64- ) ;
65- case 'SwapByIntentWithApprovalRequired' :
66- return sendTransaction ( user , swapPlan . approval ) . andThen ( ( ) =>
67- signSwapTypedDataWith ( user , swapPlan . data ) . andThen (
68- ( signature ) => {
69- return swap ( client , {
70- intent : {
71- quoteId : swapPlan . quote . quoteId ,
72- signature : signature ,
73- } ,
74- } ) ;
135+ invariant (
136+ swapPlan . __typename === 'SwapByIntentWithApprovalRequired' ,
137+ `Swap plan is not a swap by intent: ${ swapPlan . __typename } ` ,
138+ ) ;
139+ return sendTransaction ( newUser , swapPlan . approval ) . andThen ( ( ) =>
140+ signSwapTypedDataWith ( newUser , swapPlan . data ) . andThen (
141+ ( signature ) => {
142+ return swap ( client , {
143+ intent : {
144+ quoteId : swapPlan . quote . quoteId ,
145+ signature : signature ,
75146 } ,
76- ) ,
77- ) ;
78- case 'SwapByTransaction' :
79- return swap ( client , {
80- transaction : { quoteId : swapPlan . quote . quoteId } ,
81- } ) ;
82- case 'InsufficientBalanceError' :
83- throw new Error (
84- `Insufficient balance: ${ swapPlan . required . value } required.` ,
85- ) ;
86- default :
87- throw new Error (
88- `Unexpected swap plan type: ${ ( swapPlan as unknown as { __typename : string } ) . __typename } ` ,
89- ) ;
90- }
147+ } ) ;
148+ } ,
149+ ) ,
150+ ) ;
91151 } ) ;
92152
93153 assertOk ( swapResult ) ;
94154 invariant (
95155 swapResult . value . __typename === 'SwapReceipt' ,
96- ' Swap result is not a swap receipt' ,
156+ ` Swap result is not a swap receipt: ${ swapResult . value . __typename } ` ,
97157 ) ;
98158 annotate ( `Swap id: ${ swapResult . value . id } ` ) ;
99159 const status = await swapStatus ( client , { id : swapResult . value . id } ) ;
@@ -103,7 +163,7 @@ describe('Aave V4 Swap Scenarios', () => {
103163
104164 const swapPositions = await userSwaps ( client , {
105165 chainId : ETHEREUM_FORK_ID ,
106- user : evmAddress ( user . account . address ) ,
166+ user : evmAddress ( newUser . account . address ) ,
107167 } ) ;
108168 assertOk ( swapPositions ) ;
109169 expect (
@@ -117,4 +177,10 @@ describe('Aave V4 Swap Scenarios', () => {
117177 } ) ;
118178 } ) ;
119179 } ) ;
180+
181+ describe ( 'Given a user with a native token' , ( ) => {
182+ describe ( 'When the user swaps it for a wrapped native token' , ( ) => {
183+ it . todo ( 'Then the swap executes via a single transaction' ) ;
184+ } ) ;
185+ } ) ;
120186} ) ;
0 commit comments