Skip to content

Commit cdd9f8a

Browse files
authored
feat(flash-loans): support debtSwap and repayCollateral (#616)
* chore: rename collateralSwap integration * feat(flash-loans): support debtSwap and repayCollateral * chore: fix typo
1 parent c9efd22 commit cdd9f8a

File tree

8 files changed

+387
-24
lines changed

8 files changed

+387
-24
lines changed

packages/flash-loans/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
"test": "jest",
2121
"test:coverage": "jest --coverage --json --outputFile=jest.results.json && npx coveralls < ./coverage/lcov.info",
2222
"test:coverage:html": "jest --silent=false --coverage --coverageReporters html",
23-
"test:aave-collateral-integration": "npx jest --testNamePattern=^AaveFlashLoanIntegration",
23+
"test:integration:aave-collateral-swap": "npx jest --testNamePattern=^AaveFlashLoanIntegration.collateralSwap",
24+
"test:integration:aave-debt-swap": "npx jest --testNamePattern=^AaveFlashLoanIntegration.debtSwap",
25+
"test:integration:aave-repay-collateral": "npx jest --testNamePattern=^AaveFlashLoanIntegration.repayCollateral",
2426
"typecheck": "tsc --noEmit",
2527
"prepublishOnly": "npm run build"
2628
},

packages/flash-loans/src/aave/AaveCollateralSwapSdk.ts

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,20 @@ import {
1717
CollateralOrderData,
1818
CollateralParameters,
1919
CollateralPermitData,
20+
CollateralSwapOrder,
2021
CollateralSwapParams,
2122
CollateralSwapPostParams,
2223
CollateralSwapQuoteParams,
23-
CollateralSwapOrder,
2424
CollateralSwapTradeParams,
2525
EncodedOrder,
2626
FlashLoanHint,
2727
FlashLoanHookAmounts,
2828
} from './types'
2929
import {
3030
AAVE_ADAPTER_FACTORY,
31-
AAVE_COLLATERAL_SWAP_ADAPTER_HOOK,
31+
AAVE_HOOK_ADAPTER_PER_TYPE,
3232
AAVE_POOL_ADDRESS,
33+
AaveFlashLoanType,
3334
ADAPTER_DOMAIN_NAME,
3435
ADAPTER_DOMAIN_VERSION,
3536
ADAPTER_SIGNATURE_TYPES,
@@ -42,6 +43,8 @@ import {
4243
import { aaveAdapterFactoryAbi } from './abi/AaveAdapterFactory'
4344
import { collateralSwapAdapterHookAbi } from './abi/CollateralSwapAdapterHook'
4445
import { SupportedChainId } from '@cowprotocol/sdk-config'
46+
import { debtSwapAdapterAbi } from './abi/DebtSwapAdapter'
47+
import { repayWithCollateralAdapterAbi } from './abi/RepayWithCollateralAdapter'
4548

4649
/**
4750
* SDK for executing Aave flash loan operations, particularly collateral swaps.
@@ -86,12 +89,16 @@ export class AaveCollateralSwapSdk {
8689

8790
const { quoteResults, postSwapOrderFromQuote } = quoteAndPost
8891

89-
const { swapSettings, instanceAddress } = await this.getOrderPostingSettings(quoteParams, {
90-
sellAmount,
91-
buyAmount: quoteResults.amountsAndCosts.afterSlippage.buyAmount,
92-
orderToSign: quoteResults.orderToSign,
93-
collateralPermit: settings?.collateralPermit,
94-
})
92+
const { swapSettings, instanceAddress } = await this.getOrderPostingSettings(
93+
AaveFlashLoanType.CollateralSwap,
94+
quoteParams,
95+
{
96+
sellAmount,
97+
buyAmount: quoteResults.amountsAndCosts.afterSlippage.buyAmount,
98+
orderToSign: quoteResults.orderToSign,
99+
collateralPermit: settings?.collateralPermit,
100+
},
101+
)
95102

96103
const collateralParams: CollateralParameters = {
97104
instanceAddress,
@@ -178,6 +185,7 @@ export class AaveCollateralSwapSdk {
178185
* ```
179186
*/
180187
async getOrderPostingSettings(
188+
flashLoanType: AaveFlashLoanType,
181189
params: CollateralSwapTradeParams,
182190
settings: CollateralSwapOrder,
183191
): Promise<CollateralSwapPostParams> {
@@ -199,7 +207,13 @@ export class AaveCollateralSwapSdk {
199207
buyAssetAmount: buyAmount.toString(),
200208
}
201209

202-
const instanceAddress = await this.getExpectedInstanceAddress(chainId, trader, hookAmounts, encodedOrder)
210+
const instanceAddress = await this.getExpectedInstanceAddress(
211+
flashLoanType,
212+
chainId,
213+
trader,
214+
hookAmounts,
215+
encodedOrder,
216+
)
203217

204218
const flashLoanHint: FlashLoanHint = {
205219
amount, // this is actually in UNDERLYING but aave tokens are 1:1
@@ -210,6 +224,7 @@ export class AaveCollateralSwapSdk {
210224
}
211225

212226
const hooks = await this.getOrderHooks(
227+
flashLoanType,
213228
chainId,
214229
trader,
215230
instanceAddress,
@@ -322,6 +337,7 @@ export class AaveCollateralSwapSdk {
322337
}
323338

324339
async getExpectedInstanceAddress(
340+
flashLoanType: AaveFlashLoanType,
325341
chainId: SupportedChainId,
326342
trader: AccountAddress,
327343
hookAmounts: FlashLoanHookAmounts,
@@ -331,7 +347,7 @@ export class AaveCollateralSwapSdk {
331347

332348
return (await getGlobalAdapter().readContract({
333349
address: AAVE_ADAPTER_FACTORY[chainId],
334-
args: [AAVE_COLLATERAL_SWAP_ADAPTER_HOOK[chainId], hookData],
350+
args: [AAVE_HOOK_ADAPTER_PER_TYPE[flashLoanType][chainId], hookData],
335351
functionName: 'getInstanceDeterministicAddress',
336352
abi: aaveAdapterFactoryAbi,
337353
})) as AccountAddress
@@ -363,14 +379,15 @@ export class AaveCollateralSwapSdk {
363379
}
364380

365381
private getPreHookCallData(
382+
flashLoanType: AaveFlashLoanType,
366383
chainId: SupportedChainId,
367384
trader: AccountAddress,
368385
hookAmounts: FlashLoanHookAmounts,
369386
order: EncodedOrder,
370387
instanceAddress: AccountAddress,
371388
): string {
372389
const hookData = this.buildHookOrderData(trader, hookAmounts, order)
373-
const adapterImplementation = AAVE_COLLATERAL_SWAP_ADAPTER_HOOK[chainId]
390+
const adapterImplementation = AAVE_HOOK_ADAPTER_PER_TYPE[flashLoanType][chainId]
374391

375392
return getGlobalAdapter().utils.encodeFunction(aaveAdapterFactoryAbi, 'deployAndTransferFlashLoan', [
376393
adapterImplementation,
@@ -402,22 +419,52 @@ export class AaveCollateralSwapSdk {
402419
}
403420
}
404421

405-
private getPostHookCallData(collateralPermit: CollateralPermitData = EMPTY_PERMIT): string {
422+
private getFlashLoanPostHook(flashLoanType: AaveFlashLoanType, collateralPermit?: CollateralPermitData): string {
423+
if (flashLoanType === AaveFlashLoanType.DebtSwap) {
424+
return this.getDebtSwapPostHookCallData(collateralPermit)
425+
}
426+
427+
if (flashLoanType === AaveFlashLoanType.RepayCollateral) {
428+
return this.getRepayPostHookCallData(collateralPermit)
429+
}
430+
431+
return this.getCollateralSwapPostHookCallData(collateralPermit)
432+
}
433+
434+
private getCollateralSwapPostHookCallData(collateralPermit: CollateralPermitData = EMPTY_PERMIT): string {
406435
return getGlobalAdapter().utils.encodeFunction(collateralSwapAdapterHookAbi, 'collateralSwapWithFlashLoan', [
407436
collateralPermit,
408437
])
409438
}
410439

440+
private getDebtSwapPostHookCallData(collateralPermit: CollateralPermitData = EMPTY_PERMIT): string {
441+
return getGlobalAdapter().utils.encodeFunction(debtSwapAdapterAbi, 'debtSwapWithFlashLoan', [collateralPermit])
442+
}
443+
444+
private getRepayPostHookCallData(collateralPermit: CollateralPermitData = EMPTY_PERMIT): string {
445+
return getGlobalAdapter().utils.encodeFunction(repayWithCollateralAdapterAbi, 'repayDebtWithFlashLoan', [
446+
collateralPermit,
447+
])
448+
}
449+
411450
private async getOrderHooks(
451+
flashLoanType: AaveFlashLoanType,
412452
chainId: SupportedChainId,
413453
trader: AccountAddress,
414454
expectedInstanceAddress: AccountAddress,
415455
hookAmounts: FlashLoanHookAmounts,
416456
order: EncodedOrder,
417457
collateralPermit?: CollateralPermitData,
418458
): Promise<LatestAppDataDocVersion['metadata']['hooks']> {
419-
const preHookCallData = this.getPreHookCallData(chainId, trader, hookAmounts, order, expectedInstanceAddress)
420-
const postHookCallData = this.getPostHookCallData(collateralPermit)
459+
const preHookCallData = this.getPreHookCallData(
460+
flashLoanType,
461+
chainId,
462+
trader,
463+
hookAmounts,
464+
order,
465+
expectedInstanceAddress,
466+
)
467+
const postHookCallData = this.getFlashLoanPostHook(flashLoanType, collateralPermit)
421468

422469
return {
423470
pre: [
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
export const debtSwapAdapterAbi = [
2+
{
3+
type: 'function',
4+
name: 'debtSwapWithFlashLoan',
5+
inputs: [
6+
{
7+
name: 'creditDelegationSig',
8+
type: 'tuple',
9+
internalType: 'struct DataTypes.CreditDelegationSig',
10+
components: [
11+
{
12+
name: 'amount',
13+
type: 'uint256',
14+
internalType: 'uint256',
15+
},
16+
{
17+
name: 'deadline',
18+
type: 'uint256',
19+
internalType: 'uint256',
20+
},
21+
{
22+
name: 'v',
23+
type: 'uint8',
24+
internalType: 'uint8',
25+
},
26+
{
27+
name: 'r',
28+
type: 'bytes32',
29+
internalType: 'bytes32',
30+
},
31+
{
32+
name: 's',
33+
type: 'bytes32',
34+
internalType: 'bytes32',
35+
},
36+
],
37+
},
38+
],
39+
outputs: [],
40+
stateMutability: 'nonpayable',
41+
},
42+
]
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
export const repayWithCollateralAdapterAbi = [
2+
{
3+
type: 'function',
4+
name: 'repayDebtWithFlashLoan',
5+
inputs: [
6+
{
7+
name: 'erc20Permit',
8+
type: 'tuple',
9+
internalType: 'struct DataTypes.Permit',
10+
components: [
11+
{
12+
name: 'amount',
13+
type: 'uint256',
14+
internalType: 'uint256',
15+
},
16+
{
17+
name: 'deadline',
18+
type: 'uint256',
19+
internalType: 'uint256',
20+
},
21+
{
22+
name: 'v',
23+
type: 'uint8',
24+
internalType: 'uint8',
25+
},
26+
{
27+
name: 'r',
28+
type: 'bytes32',
29+
internalType: 'bytes32',
30+
},
31+
{
32+
name: 's',
33+
type: 'bytes32',
34+
internalType: 'bytes32',
35+
},
36+
],
37+
},
38+
],
39+
outputs: [],
40+
stateMutability: 'nonpayable',
41+
},
42+
]

packages/flash-loans/src/aave/integration.test.ts renamed to packages/flash-loans/src/aave/collateralSwap.integration.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ import { OrderKind } from '@cowprotocol/sdk-order-book'
99

1010
import { AaveCollateralSwapSdk } from './AaveCollateralSwapSdk'
1111
import { AccountAddress } from '@cowprotocol/sdk-common'
12-
import { HASH_ZERO } from './const'
12+
import { AaveFlashLoanType, HASH_ZERO } from './const'
1313

1414
// =================== Config ===================
1515
const RPC_URL = 'https://rpc.gnosis.gateway.fm'
1616
const PRIVATE_KEY = process.env.PRIVATE_KEY
1717
// ===============================================================
1818

19-
describe('AaveFlashLoanIntegration', () => {
19+
describe('AaveFlashLoanIntegration.collateralSwap', () => {
2020
it.skip('Test AaveFlashLoanSdk collateralSwap on Gnosis Chain with swap', async () => {
2121
const chainId = SupportedChainId.GNOSIS_CHAIN
2222

@@ -136,6 +136,7 @@ describe('AaveFlashLoanIntegration', () => {
136136
)
137137

138138
const orderPostParams = await flashLoanSdk.getOrderPostingSettings(
139+
AaveFlashLoanType.CollateralSwap,
139140
{
140141
chainId,
141142
validTo,

packages/flash-loans/src/aave/const.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,24 @@ import { CollateralPermitData } from './types'
33

44
export const HASH_ZERO = '0x0000000000000000000000000000000000000000000000000000000000000000'
55

6+
export enum AaveFlashLoanType {
7+
CollateralSwap = 'CollateralSwap',
8+
DebtSwap = 'DebtSwap',
9+
RepayCollateral = 'RepayCollateral',
10+
}
11+
612
// See https://search.onaave.com/?q=sepolia
713
export const AAVE_POOL_ADDRESS = mapAddressToSupportedNetworks('0xb50201558B00496A145fE76f7424749556E326D8')
8-
export const AAVE_ADAPTER_FACTORY = {
9-
...mapAddressToSupportedNetworks('0x1186B5ad42E3e6d6c6901FC53b4A367540E6EcFE'),
10-
[SupportedChainId.GNOSIS_CHAIN]: '0x889ee28C0a8a41a312546A8eeD77b4b097C84016',
11-
}
12-
export const AAVE_COLLATERAL_SWAP_ADAPTER_HOOK = {
13-
...mapAddressToSupportedNetworks('0xe80eE1e73f120b1106179Ae3D582CA4Fd768d517'),
14-
[SupportedChainId.GNOSIS_CHAIN]: '0x0aeC794e544B81D96149a4C8C1cC57c6F31A978A',
14+
export const AAVE_ADAPTER_FACTORY = mapAddressToSupportedNetworks('0x889ee28C0a8a41a312546A8eeD77b4b097C84016')
15+
16+
const AAVE_COLLATERAL_SWAP_ADAPTER_HOOK = mapAddressToSupportedNetworks('0x0aeC794e544B81D96149a4C8C1cC57c6F31A978A')
17+
const AAVE_DEBT_SWAP_ADAPTER_HOOK = mapAddressToSupportedNetworks('0x2d13ADCFa398073d7406e5e1aF3dD14663cdBF30')
18+
const AAVE_REPAY_COLLATERAL_ADAPTER_HOOK = mapAddressToSupportedNetworks('0x193fd444802D6BC18a9AE0613D33C024F16A9dDC')
19+
20+
export const AAVE_HOOK_ADAPTER_PER_TYPE: Record<AaveFlashLoanType, Record<SupportedChainId, string>> = {
21+
[AaveFlashLoanType.CollateralSwap]: AAVE_COLLATERAL_SWAP_ADAPTER_HOOK,
22+
[AaveFlashLoanType.DebtSwap]: AAVE_DEBT_SWAP_ADAPTER_HOOK,
23+
[AaveFlashLoanType.RepayCollateral]: AAVE_REPAY_COLLATERAL_ADAPTER_HOOK,
1524
}
1625

1726
export const DEFAULT_HOOK_GAS_LIMIT = {

0 commit comments

Comments
 (0)