Skip to content

Commit 2c63536

Browse files
authored
Merge pull request #223 from aave/cesare/aave-3208-sdk-support-swap-position-adapters
feat: withdraw and swap on-the-fly
2 parents c6569d3 + b06776a commit 2c63536

File tree

6 files changed

+337
-1
lines changed

6 files changed

+337
-1
lines changed

.changeset/orange-zebras-brake.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@aave/graphql": patch
3+
"@aave/client": patch
4+
"@aave/react": patch
5+
---
6+
7+
**feat:** `withdrawSwapQuote` action, `useWithdrawSwapQuote` and `useWithdrawSwap` hooks.

packages/client/src/actions/swap.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import type {
1515
PrepareSwapCancelResult,
1616
PrepareTokenSwapRequest,
1717
PrepareTokenSwapResult,
18+
PrepareWithdrawSwapRequest,
19+
PrepareWithdrawSwapResult,
1820
SwapCancelled,
1921
SwapExecutionPlan,
2022
SwapExpired,
@@ -42,6 +44,7 @@ import {
4244
SwapQuoteQuery,
4345
SwapStatusQuery,
4446
UserSwapsQuery,
47+
WithdrawSwapQuoteQuery,
4548
} from '@aave/graphql';
4649
import { ResultAsync } from '@aave/types';
4750
import type { AaveClient } from '../AaveClient';
@@ -251,6 +254,38 @@ export function repayWithSupplyQuote(
251254
);
252255
}
253256

257+
/**
258+
* @experimental
259+
* Fetches a withdraw swap quote for withdrawing deposits and swapping on the fly.
260+
*
261+
* ```ts
262+
* const result = await withdrawSwapQuote(client, {
263+
* market: {
264+
* position: userSupplyItemId('position_123'),
265+
* buyReserve: reserveId('reserve_456'),
266+
* amount: bigDecimal('1000'),
267+
* user: evmAddress('0x742d35cc...'),
268+
* },
269+
* });
270+
* ```
271+
*
272+
* @param client - Aave client.
273+
* @param request - The withdraw swap request parameters.
274+
* @param options - The query options.
275+
* @returns The withdraw swap result with quote, approvals, and preview.
276+
*/
277+
export function withdrawSwapQuote(
278+
client: AaveClient,
279+
request: PrepareWithdrawSwapRequest,
280+
options: Required<CurrencyQueryOptions> = DEFAULT_QUERY_OPTIONS,
281+
): ResultAsync<PrepareWithdrawSwapResult, UnexpectedError> {
282+
return client.query(
283+
WithdrawSwapQuoteQuery,
284+
{ request, currency: options.currency },
285+
{ batch: false },
286+
);
287+
}
288+
254289
/**
255290
* @experimental
256291
* Prepares a position swap by obtaining the typed data for signing.

packages/graphql/src/fragments/swaps.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,21 @@ export const PrepareRepayWithSupplyResultFragment: FragmentDocumentFor<
482482
[PositionSwapByIntentApprovalsRequiredFragment],
483483
);
484484

485+
export type PrepareWithdrawSwapResult = PositionSwapByIntentApprovalsRequired;
486+
487+
export const PrepareWithdrawSwapResultFragment: FragmentDocumentFor<
488+
PrepareWithdrawSwapResult,
489+
'PrepareWithdrawSwapResult'
490+
> = graphql(
491+
`fragment PrepareWithdrawSwapResult on PrepareWithdrawSwapResult {
492+
__typename
493+
... on PositionSwapByIntentApprovalsRequired {
494+
...PositionSwapByIntentApprovalsRequired
495+
}
496+
}`,
497+
[PositionSwapByIntentApprovalsRequiredFragment],
498+
);
499+
485500
export type PreparePositionSwapResult = SwapByIntent;
486501

487502
export const PreparePositionSwapResultFragment: FragmentDocumentFor<

packages/graphql/src/swaps.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
PrepareSupplySwapResultFragment,
88
PrepareSwapCancelResultFragment,
99
PrepareTokenSwapResultFragment,
10+
PrepareWithdrawSwapResultFragment,
1011
SwapExecutionPlanFragment,
1112
SwapQuoteFragment,
1213
SwapStatusFragment,
@@ -159,6 +160,21 @@ export type PrepareRepayWithSupplyRequest = RequestOf<
159160
typeof RepayWithSupplyQuoteQuery
160161
>;
161162

163+
/**
164+
* @internal
165+
*/
166+
export const WithdrawSwapQuoteQuery = graphql(
167+
`query WithdrawSwapQuote($request: PrepareWithdrawSwapRequest!, $currency: Currency!) {
168+
value: withdrawSwapQuote(request: $request) {
169+
...PrepareWithdrawSwapResult
170+
}
171+
}`,
172+
[PrepareWithdrawSwapResultFragment],
173+
);
174+
export type PrepareWithdrawSwapRequest = RequestOf<
175+
typeof WithdrawSwapQuoteQuery
176+
>;
177+
162178
/**
163179
* @internal
164180
*/

packages/react/src/swap.test.ts

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ import {
1111
PreparePositionSwapQuery,
1212
type PrepareRepayWithSupplyRequest,
1313
type PrepareSupplySwapRequest,
14+
type PrepareWithdrawSwapRequest,
1415
RepayWithSupplyQuoteQuery,
1516
SupplySwapQuoteQuery,
1617
SwapMutation,
18+
WithdrawSwapQuoteQuery,
1719
} from '@aave/graphql';
1820
import {
1921
makePositionSwapAdapterContractApproval,
@@ -34,7 +36,12 @@ import {
3436
describe,
3537
it,
3638
} from 'vitest';
37-
import { useBorrowSwap, useRepayWithSupply, useSupplySwap } from './swap';
39+
import {
40+
useBorrowSwap,
41+
useRepayWithSupply,
42+
useSupplySwap,
43+
useWithdrawSwap,
44+
} from './swap';
3845
import { renderHookWithinContext } from './test-utils';
3946
import { useSendTransaction } from './viem';
4047

@@ -303,4 +310,76 @@ describe('Given the swap hooks', () => {
303310
assertOk(result);
304311
});
305312
});
313+
314+
describe(`When using the '${useWithdrawSwap.name}' hook`, () => {
315+
beforeEach(() => {
316+
server.use(
317+
api.query(WithdrawSwapQuoteQuery, () =>
318+
msw.HttpResponse.json({
319+
data: {
320+
value: {
321+
__typename: 'PositionSwapByIntentApprovalsRequired',
322+
quote: makeSwapQuote(),
323+
approvals: [
324+
makePositionSwapPositionManagerApproval({
325+
byTransaction: dummyTransactionRequest,
326+
}),
327+
makePositionSwapAdapterContractApproval({
328+
byTransaction: dummyTransactionRequest,
329+
}),
330+
],
331+
},
332+
},
333+
}),
334+
),
335+
);
336+
});
337+
338+
it('Then it should support position swap with position manager and adapter contract approvals via signatures', async () => {
339+
const {
340+
result: {
341+
current: [swap],
342+
},
343+
} = renderHookWithinContext(() =>
344+
useWithdrawSwap((plan) => {
345+
switch (plan.__typename) {
346+
case 'PositionSwapPositionManagerApproval':
347+
case 'PositionSwapAdapterContractApproval':
348+
return signSwapTypedDataWith(walletClient, plan.bySignature);
349+
350+
case 'SwapByIntent':
351+
return signSwapTypedDataWith(walletClient, plan.data);
352+
}
353+
}),
354+
);
355+
356+
const result = await swap({} as PrepareWithdrawSwapRequest);
357+
358+
assertOk(result);
359+
});
360+
361+
it('Then it should support position swap with position manager and adapter contract approvals via transactions', async () => {
362+
const {
363+
result: {
364+
current: [swap],
365+
},
366+
} = renderHookWithinContext(() => {
367+
const [sendTransaction] = useSendTransaction(walletClient);
368+
return useWithdrawSwap((plan) => {
369+
switch (plan.__typename) {
370+
case 'PositionSwapPositionManagerApproval':
371+
case 'PositionSwapAdapterContractApproval':
372+
return sendTransaction(plan.byTransaction);
373+
374+
case 'SwapByIntent':
375+
return signSwapTypedDataWith(walletClient, plan.data);
376+
}
377+
});
378+
});
379+
380+
const result = await swap({} as PrepareWithdrawSwapRequest);
381+
382+
assertOk(result);
383+
});
384+
});
306385
});

0 commit comments

Comments
 (0)