-
Notifications
You must be signed in to change notification settings - Fork 15
/
logic.flash-loan.ts
139 lines (112 loc) · 5.37 KB
/
logic.flash-loan.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import { AaveV3FlashLoanCallback__factory, Pool__factory } from './contracts';
import { BigNumberish, constants } from 'ethers';
import { InterestRateMode } from './types';
import { Service } from './service';
import * as common from '@protocolink/common';
import * as core from '@protocolink/core';
import { getContractAddress, supportedChainIds } from './configs';
import invariant from 'tiny-invariant';
export type FlashLoanLogicTokenList = common.Token[];
export type FlashLoanLogicParams = core.FlashLoanParams;
export type FlashLoanLogicQuotation = core.FlashLoanQuotation;
export type FlashLoanLogicFields = core.FlashLoanFields<{ referralCode?: number }>;
export class FlashLoanLogic
extends core.Logic
implements core.LogicTokenListInterface, core.LogicOracleInterface, core.LogicBuilderInterface
{
static id = 'flash-loan';
static protocolId = 'aave-v3';
static readonly supportedChainIds = supportedChainIds;
get callbackAddress() {
return getContractAddress(this.chainId, 'AaveV3FlashLoanCallback');
}
async calcCallbackFee(loan: common.TokenAmount) {
const callback = AaveV3FlashLoanCallback__factory.connect(this.callbackAddress, this.provider);
const feeRate = await callback.feeRate();
const callbackFee = new common.TokenAmount(loan.token).setWei(common.calcFee(loan.amountWei, feeRate.toNumber()));
return callbackFee;
}
async getTokenList() {
const service = new Service(this.chainId, this.provider);
const tokens = await service.getAssets();
const { assetInfos } = await service.getFlashLoanConfiguration(tokens);
const tokenList: FlashLoanLogicTokenList = [];
for (let i = 0; i < assetInfos.length; i++) {
const { isActive, isPaused, isFlashLoanEnabled } = assetInfos[i];
if (!isActive || isPaused || !isFlashLoanEnabled) continue;
tokenList.push(tokens[i]);
}
return tokenList;
}
// https://github.com/aave/aave-v3-core/blob/v1.19.1/contracts/protocol/pool/Pool.sol#L386
// https://github.com/aave/aave-v3-core/blob/v1.19.1/contracts/protocol/libraries/logic/FlashLoanLogic.sol#L70
// https://github.com/aave/aave-v3-core/blob/v1.19.1/contracts/protocol/libraries/logic/FlashLoanLogic.sol#L94
async quote(params: FlashLoanLogicParams) {
const assets = core.isFlashLoanLoanParams(params)
? params.loans.map(({ token }) => token)
: params.repays.map(({ token }) => token);
const service = new Service(this.chainId, this.provider);
const { feeBps, assetInfos } = await service.getFlashLoanConfiguration(assets);
let loans: common.TokenAmounts;
let repays: common.TokenAmounts;
if (core.isFlashLoanLoanParams(params)) {
({ loans } = params);
repays = new common.TokenAmounts();
for (let i = 0; i < loans.length; i++) {
const loan = loans.at(i);
const { isActive, isPaused, isFlashLoanEnabled, availableToBorrow } = assetInfos[i];
invariant(isActive, `asset is not active: ${loan.token.address}`);
invariant(!isPaused, `asset is paused: ${loan.token.address}`);
invariant(isFlashLoanEnabled, `asset can not be used in flash loan: ${loan.token.address}`);
invariant(availableToBorrow.gte(loan), `insufficient borrowing capacity for the asset: ${loan.token.address}`);
const feeAmountWei = common.calcFee(loan.amountWei, feeBps, 'round');
const fee = new common.TokenAmount(loan.token).setWei(feeAmountWei);
const repay = loan.clone().add(fee);
repays.add(repay);
}
} else {
loans = new common.TokenAmounts();
repays = new common.TokenAmounts();
for (let i = 0; i < params.repays.length; i++) {
const repay = params.repays.at(i);
const loanAmountWei = common.reverseAmountWithFee(repay.amountWei, feeBps);
const loan = new common.TokenAmount(repay.token).setWei(loanAmountWei);
loans.add(loan);
const { isActive, isPaused, isFlashLoanEnabled, availableToBorrow } = assetInfos[i];
invariant(isActive, `asset is not active: ${loan.token.address}`);
invariant(!isPaused, `asset is paused: ${loan.token.address}`);
invariant(isFlashLoanEnabled, `asset can not be used in flash loan: ${loan.token.address}`);
invariant(availableToBorrow.gte(loan), `insufficient borrowing capacity for the asset: ${loan.token.address}`);
const feeAmountWei = common.calcFee(loan.amountWei, feeBps, 'round');
const fee = new common.TokenAmount(loan.token).setWei(feeAmountWei);
repays.add(loan.clone().add(fee));
}
}
const quotation: FlashLoanLogicQuotation = { loans, repays, feeBps };
return quotation;
}
async build(fields: FlashLoanLogicFields) {
const { loans, params, referralCode = 0 } = fields;
const service = new Service(this.chainId, this.provider);
const to = await service.getPoolAddress();
const assets: string[] = [];
const amounts: BigNumberish[] = [];
const modes: number[] = [];
loans.forEach((loan) => {
assets.push(loan.token.address);
amounts.push(loan.amountWei);
modes.push(InterestRateMode.none);
});
const data = Pool__factory.createInterface().encodeFunctionData('flashLoan', [
this.callbackAddress,
assets,
amounts,
modes,
constants.AddressZero,
params,
referralCode,
]);
const callback = this.callbackAddress;
return core.newLogic({ to, data, callback });
}
}