Skip to content

Commit

Permalink
feat: balancer v2 flash loan logic add quote func
Browse files Browse the repository at this point in the history
  • Loading branch information
chouandy committed Aug 1, 2023
1 parent 05d29c9 commit 874029a
Show file tree
Hide file tree
Showing 29 changed files with 145 additions and 83 deletions.
5 changes: 5 additions & 0 deletions .changeset/silent-rockets-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@protocolink/logics': patch
---

balancer v2 flash loan logic add quote func
10 changes: 9 additions & 1 deletion src/logics/aave-v2/logic.flash-loan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ export type FlashLoanLogicTokenList = common.Token[];

export type FlashLoanLogicParams = core.TokensOutFields;

export type FlashLoanLogicQuotation = {
loans: common.TokenAmounts;
repays: common.TokenAmounts;
fees: common.TokenAmounts;
feeBps: number;
};

export type FlashLoanLogicFields = core.FlashLoanFields<{ referralCode?: number }>;

@core.LogicDefinitionDecorator()
Expand Down Expand Up @@ -45,8 +52,9 @@ export class FlashLoanLogic extends core.Logic implements core.LogicTokenListInt
const repay = loan.clone().add(fee);
repays.add(repay);
}
const quotation: FlashLoanLogicQuotation = { loans, repays, fees, feeBps };

return { loans, repays, fees, feeBps };
return quotation;
}

async build(fields: FlashLoanLogicFields) {
Expand Down
10 changes: 9 additions & 1 deletion src/logics/aave-v3/logic.flash-loan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ export type FlashLoanLogicTokenList = common.Token[];

export type FlashLoanLogicParams = core.TokensOutFields;

export type FlashLoanLogicQuotation = {
loans: common.TokenAmounts;
repays: common.TokenAmounts;
fees: common.TokenAmounts;
feeBps: number;
};

export type FlashLoanLogicFields = core.FlashLoanFields<{ referralCode?: number }>;

@core.LogicDefinitionDecorator()
Expand Down Expand Up @@ -50,8 +57,9 @@ export class FlashLoanLogic
const repay = loan.clone().add(fee);
repays.add(repay);
}
const quotation: FlashLoanLogicQuotation = { loans, repays, fees, feeBps };

return { loans, repays, fees, feeBps };
return quotation;
}

async build(fields: FlashLoanLogicFields) {
Expand Down
36 changes: 36 additions & 0 deletions src/logics/balancer-v2/logic.flash-loan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,19 @@ import { axios } from 'src/utils';
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.TokensOutFields;

export type FlashLoanLogicQuotation = {
loans: common.TokenAmounts;
repays: common.TokenAmounts;
fees: common.TokenAmounts;
feeBps: number;
};

export type FlashLoanLogicFields = core.FlashLoanFields;

@core.LogicDefinitionDecorator()
Expand All @@ -30,6 +40,32 @@ export class FlashLoanLogic extends core.Logic implements core.LogicTokenListInt
return tokenList;
}

async quote(params: FlashLoanLogicParams) {
const { outputs: loans } = params;

const vaultAddress = getContractAddress(this.chainId, 'Vault');
const calls: common.Multicall2.CallStruct[] = loans.map((loan) => ({
target: loan.token.address,
callData: this.erc20Iface.encodeFunctionData('balanceOf', [vaultAddress]),
}));
const { returnData } = await this.multicall2.callStatic.aggregate(calls);

const repays = new common.TokenAmounts();
const fees = new common.TokenAmounts();
for (let i = 0; i < loans.length; i++) {
const loan = loans.at(i);
const [balance] = this.erc20Iface.decodeFunctionResult('balanceOf', returnData[i]);
const avaliableToBorrow = new common.TokenAmount(loan.token).setWei(balance);
invariant(avaliableToBorrow.gte(loan), `insufficient borrowing capacity for the asset: ${loan.token.address}`);

repays.add(loan.token, loan.amount);
fees.add(loan.token, '0');
}
const quotation: FlashLoanLogicQuotation = { loans, repays, fees, feeBps: 0 };

return quotation;
}

async build(fields: FlashLoanLogicFields) {
const { outputs, params } = fields;

Expand Down
4 changes: 2 additions & 2 deletions test/logics/aave-v2/borrow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ describe('Test AaveV2 Borrow Logic', function () {

// 3. build router logics
const routerLogics: core.IParam.LogicStruct[] = [];
const logicAaveV2Borrow = new aavev2.BorrowLogic(chainId);
routerLogics.push(await logicAaveV2Borrow.build({ output, interestRateMode }, { account: user.address }));
const aaveV2BorrowLogic = new aavev2.BorrowLogic(chainId);
routerLogics.push(await aaveV2BorrowLogic.build({ output, interestRateMode }, { account: user.address }));

// 4. send router tx
const transactionRequest = core.newRouterExecuteTransactionRequest({ chainId, routerLogics, tokensReturn });
Expand Down
6 changes: 3 additions & 3 deletions test/logics/aave-v2/deposit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ describe('Test AaveV2 Deposit Logic', function () {
testCases.forEach(({ input, tokenOut, balanceBps }, i) => {
it(`case ${i + 1}`, async function () {
// 1. get output
const logicAaveV2Deposit = new aavev2.DepositLogic(chainId);
const { output } = await logicAaveV2Deposit.quote({ input, tokenOut });
const aaveV2DepositLogic = new aavev2.DepositLogic(chainId);
const { output } = await aaveV2DepositLogic.quote({ input, tokenOut });

// 2. build funds, tokensReturn
const tokensReturn = [output.token.elasticAddress];
Expand All @@ -69,7 +69,7 @@ describe('Test AaveV2 Deposit Logic', function () {
// 3. build router logics
const erc20Funds = funds.erc20;
const routerLogics = await utils.getPermitAndPullTokenRouterLogics(chainId, user, erc20Funds);
routerLogics.push(await logicAaveV2Deposit.build({ input, output, balanceBps }, { account: user.address }));
routerLogics.push(await aaveV2DepositLogic.build({ input, output, balanceBps }, { account: user.address }));

// 4. send router tx
const transactionRequest = core.newRouterExecuteTransactionRequest({
Expand Down
7 changes: 3 additions & 4 deletions test/logics/aave-v2/flash-loan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ describe('Test AaveV2 FlashLoan Logic', function () {
testCases.forEach(({ outputs }, i) => {
it(`case ${i + 1}`, async function () {
// 1. get flash loan quotation
const logicAaveV3FlashLoan = new aavev2.FlashLoanLogic(chainId);
const { loans, repays, fees } = await logicAaveV3FlashLoan.quote({ outputs });
const aaveV2FlashLoanLogic = new aavev2.FlashLoanLogic(chainId);
const { loans, repays, fees } = await aaveV2FlashLoanLogic.quote({ outputs });

// 2. build funds and router logics for flash loan by flash loan fee
const funds = new common.TokenAmounts();
Expand All @@ -53,8 +53,7 @@ describe('Test AaveV2 FlashLoan Logic', function () {
const routerLogics = await utils.getPermitAndPullTokenRouterLogics(chainId, user, erc20Funds);

const params = core.newCallbackParams(flashLoanRouterLogics);
const logicAaveV2FlashLoan = new aavev2.FlashLoanLogic(chainId);
routerLogics.push(await logicAaveV2FlashLoan.build({ outputs: loans, params }));
routerLogics.push(await aaveV2FlashLoanLogic.build({ outputs: loans, params }));

// 4. send router tx
const transactionRequest = core.newRouterExecuteTransactionRequest({ chainId, routerLogics });
Expand Down
8 changes: 4 additions & 4 deletions test/logics/aave-v2/repay.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ describe('Test AaveV2 Repay Logic', function () {
await helpers.borrow(chainId, user, borrow, interestRateMode);

// 2. get user debt
const logicAaveV2Repay = new aavev2.RepayLogic(chainId, hre.ethers.provider);
let quotation = await logicAaveV2Repay.quote({ borrower: user.address, tokenIn: borrow.token, interestRateMode });
const aaveV2RepayLogic = new aavev2.RepayLogic(chainId, hre.ethers.provider);
let quotation = await aaveV2RepayLogic.quote({ borrower: user.address, tokenIn: borrow.token, interestRateMode });
const { input } = quotation;

// 3. build funds and tokensReturn
Expand All @@ -130,7 +130,7 @@ describe('Test AaveV2 Repay Logic', function () {
const erc20Funds = funds.erc20;
const routerLogics = await utils.getPermitAndPullTokenRouterLogics(chainId, user, erc20Funds);

routerLogics.push(await logicAaveV2Repay.build({ input, interestRateMode, borrower: user.address, balanceBps }));
routerLogics.push(await aaveV2RepayLogic.build({ input, interestRateMode, borrower: user.address, balanceBps }));

// 5. send router tx
const transactionRequest = core.newRouterExecuteTransactionRequest({
Expand All @@ -143,7 +143,7 @@ describe('Test AaveV2 Repay Logic', function () {
await expect(user.address).to.changeBalance(input.token, -input.amount, 200);

// 6. check user's debt should be zero
quotation = await logicAaveV2Repay.quote({ borrower: user.address, tokenIn: borrow.token, interestRateMode });
quotation = await aaveV2RepayLogic.quote({ borrower: user.address, tokenIn: borrow.token, interestRateMode });
expect(quotation.input.amountWei).to.eq(0);
});
});
Expand Down
6 changes: 3 additions & 3 deletions test/logics/aave-v2/withdraw.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ describe('Test AaveV2 Withdraw Logic', function () {
await helpers.deposit(chainId, user, assetsAmount);

// 2. get output
const logicAaveV2Withdraw = new aavev2.WithdrawLogic(chainId);
const { output } = await logicAaveV2Withdraw.quote({ input, tokenOut });
const aaveV2WithdrawLogic = new aavev2.WithdrawLogic(chainId);
const { output } = await aaveV2WithdrawLogic.quote({ input, tokenOut });

// 3. build funds, tokensReturn
const tokensReturn = [output.token.elasticAddress];
Expand All @@ -74,7 +74,7 @@ describe('Test AaveV2 Withdraw Logic', function () {
// 4. build router logics
const erc20Funds = funds.erc20;
const routerLogics = await utils.getPermitAndPullTokenRouterLogics(chainId, user, erc20Funds);
routerLogics.push(await logicAaveV2Withdraw.build({ input, output, balanceBps }, { account: user.address }));
routerLogics.push(await aaveV2WithdrawLogic.build({ input, output, balanceBps }, { account: user.address }));

// 5. send router tx
const transactionRequest = core.newRouterExecuteTransactionRequest({ chainId, routerLogics, tokensReturn });
Expand Down
4 changes: 2 additions & 2 deletions test/logics/aave-v3/borrow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ describe('Test AaveV3 Borrow Logic', function () {
// 3. build router logics
const routerLogics: core.IParam.LogicStruct[] = [];

const logicAaveV3Borrow = new aavev3.BorrowLogic(chainId);
routerLogics.push(await logicAaveV3Borrow.build({ output, interestRateMode }, { account: user.address }));
const aaveV3BorrowLogic = new aavev3.BorrowLogic(chainId);
routerLogics.push(await aaveV3BorrowLogic.build({ output, interestRateMode }, { account: user.address }));

// 4. send router tx
const transactionRequest = core.newRouterExecuteTransactionRequest({ chainId, routerLogics, tokensReturn });
Expand Down
6 changes: 3 additions & 3 deletions test/logics/aave-v3/flash-loan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ describe('Test AaveV3 FlashLoan Logic', function () {
testCases.forEach(({ outputs }, i) => {
it(`case ${i + 1}`, async function () {
// 1. get flash loan quotation
const logicAaveV3FlashLoan = new aavev3.FlashLoanLogic(chainId);
const { loans, repays, fees } = await logicAaveV3FlashLoan.quote({ outputs });
const aaveV3FlashLoanLogic = new aavev3.FlashLoanLogic(chainId);
const { loans, repays, fees } = await aaveV3FlashLoanLogic.quote({ outputs });

// 2. build funds and router logics for flash loan by flash loan fee
const funds = new common.TokenAmounts();
Expand All @@ -53,7 +53,7 @@ describe('Test AaveV3 FlashLoan Logic', function () {
const routerLogics = await utils.getPermitAndPullTokenRouterLogics(chainId, user, erc20Funds);

const params = core.newCallbackParams(flashLoanRouterLogics);
routerLogics.push(await logicAaveV3FlashLoan.build({ outputs: loans, params }));
routerLogics.push(await aaveV3FlashLoanLogic.build({ outputs: loans, params }));

// 4. send router tx
const transactionRequest = core.newRouterExecuteTransactionRequest({ chainId, routerLogics });
Expand Down
8 changes: 4 additions & 4 deletions test/logics/aave-v3/repay.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ describe('Test AaveV3 Repay Logic', function () {
await helpers.borrow(chainId, user, borrow, interestRateMode);

// 2. get user debt
const logicAaveV3Repay = new aavev3.RepayLogic(chainId, hre.ethers.provider);
let quotation = await logicAaveV3Repay.quote({ borrower: user.address, tokenIn: borrow.token, interestRateMode });
const aaveV3RepayLogic = new aavev3.RepayLogic(chainId, hre.ethers.provider);
let quotation = await aaveV3RepayLogic.quote({ borrower: user.address, tokenIn: borrow.token, interestRateMode });
const { input } = quotation;

// 3. build funds and tokensReturn
Expand All @@ -91,7 +91,7 @@ describe('Test AaveV3 Repay Logic', function () {
const erc20Funds = funds.erc20;
const routerLogics = await utils.getPermitAndPullTokenRouterLogics(chainId, user, erc20Funds);

routerLogics.push(await logicAaveV3Repay.build({ input, interestRateMode, borrower: user.address, balanceBps }));
routerLogics.push(await aaveV3RepayLogic.build({ input, interestRateMode, borrower: user.address, balanceBps }));

// 5. send router tx
const transactionRequest = core.newRouterExecuteTransactionRequest({
Expand All @@ -104,7 +104,7 @@ describe('Test AaveV3 Repay Logic', function () {
await expect(user.address).to.changeBalance(input.token, -input.amount, 200);

// 6. check user's debt should be zero
quotation = await logicAaveV3Repay.quote({ borrower: user.address, tokenIn: borrow.token, interestRateMode });
quotation = await aaveV3RepayLogic.quote({ borrower: user.address, tokenIn: borrow.token, interestRateMode });
expect(quotation.input.amountWei).to.eq(0);
});
});
Expand Down
6 changes: 3 additions & 3 deletions test/logics/aave-v3/supply.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ describe('Test AaveV3 Supply Logic', function () {
testCases.forEach(({ input, tokenOut, balanceBps }, i) => {
it(`case ${i + 1}`, async function () {
// 1. get output
const logicAavev3Supply = new aavev3.SupplyLogic(chainId);
const { output } = await logicAavev3Supply.quote({ input, tokenOut });
const aavev3SupplyLogic = new aavev3.SupplyLogic(chainId);
const { output } = await aavev3SupplyLogic.quote({ input, tokenOut });

// 2. build funds, tokensReturn
const tokensReturn = [output.token.elasticAddress];
Expand All @@ -69,7 +69,7 @@ describe('Test AaveV3 Supply Logic', function () {
// 3. build router logics
const erc20Funds = funds.erc20;
const routerLogics = await utils.getPermitAndPullTokenRouterLogics(chainId, user, erc20Funds);
routerLogics.push(await logicAavev3Supply.build({ input, output, balanceBps }, { account: user.address }));
routerLogics.push(await aavev3SupplyLogic.build({ input, output, balanceBps }, { account: user.address }));

// 4. send router tx
const transactionRequest = core.newRouterExecuteTransactionRequest({
Expand Down
6 changes: 3 additions & 3 deletions test/logics/aave-v3/withdraw.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ describe('Test AaveV3 Withdraw Logic', function () {
await helpers.supply(chainId, user, assetsAmount);

// 2. get output
const logicAaveV3Withdraw = new aavev3.WithdrawLogic(chainId);
const { output } = await logicAaveV3Withdraw.quote({ input, tokenOut });
const aaveV3WithdrawLogic = new aavev3.WithdrawLogic(chainId);
const { output } = await aaveV3WithdrawLogic.quote({ input, tokenOut });

// 3. build funds, tokensReturn
const tokensReturn = [output.token.elasticAddress];
Expand All @@ -74,7 +74,7 @@ describe('Test AaveV3 Withdraw Logic', function () {
// 4. build router logics
const erc20Funds = funds.erc20;
const routerLogics = await utils.getPermitAndPullTokenRouterLogics(chainId, user, erc20Funds);
routerLogics.push(await logicAaveV3Withdraw.build({ input, output, balanceBps }, { account: user.address }));
routerLogics.push(await aaveV3WithdrawLogic.build({ input, output, balanceBps }, { account: user.address }));

// 5. send router tx
const transactionRequest = core.newRouterExecuteTransactionRequest({ chainId, routerLogics, tokensReturn });
Expand Down
20 changes: 13 additions & 7 deletions test/logics/balancer-v2/flash-loan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,32 @@ describe('Test BalancerV2 FlashLoan Logic', function () {

testCases.forEach(({ outputs }, i) => {
it(`case ${i + 1}`, async function () {
// 1. build funds and router logics for flash loan
// 1. get flash loan quotation
const aaveV3FlashLoanLogic = new balancerv2.FlashLoanLogic(chainId);
const { loans, repays, fees } = await aaveV3FlashLoanLogic.quote({ outputs });

// 2. build funds and router logics for flash loan
const funds = new common.TokenAmounts();
const flashLoanRouterLogics: core.IParam.LogicStruct[] = [];
const utilitySendTokenLogic = new utility.SendTokenLogic(chainId);
for (const output of outputs.toArray()) {
for (let i = 0; i < fees.length; i++) {
funds.add(fees.at(i).clone());
flashLoanRouterLogics.push(
await utilitySendTokenLogic.build({
input: output,
input: repays.at(i),
recipient: balancerv2.getContractAddress(chainId, 'BalancerV2FlashLoanCallback'),
})
);
}

// 2. build router logics
// 3. build router logics
const routerLogics: core.IParam.LogicStruct[] = [];

const userData = core.newCallbackParams(flashLoanRouterLogics);
const logicBalancerV2FlashLoan = new balancerv2.FlashLoanLogic(chainId);
routerLogics.push(await logicBalancerV2FlashLoan.build({ outputs, params: userData }));
const balancerV2FlashLoanLogic = new balancerv2.FlashLoanLogic(chainId);
routerLogics.push(await balancerV2FlashLoanLogic.build({ outputs: loans, params: userData }));

// 3. send router tx
// 4. send router tx
const transactionRequest = core.newRouterExecuteTransactionRequest({ chainId, routerLogics });
await expect(user.sendTransaction(transactionRequest)).to.not.be.reverted;
});
Expand Down
6 changes: 3 additions & 3 deletions test/logics/compound-v2/claim.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@ describe('Test CompoundV2 Claim Logic', function () {

// 2. get allocated COMP amount after 1000 blocks
await hrehelpers.mine(1000);
const logicCompoundV2Claim = new compoundv2.ClaimLogic(chainId, hre.ethers.provider);
const { output } = await logicCompoundV2Claim.quote({ owner: owner.address });
const compoundV2ClaimLogic = new compoundv2.ClaimLogic(chainId, hre.ethers.provider);
const { output } = await compoundV2ClaimLogic.quote({ owner: owner.address });
expect(output.amountWei).to.be.gt(0);

// 3. build router logics
const routerLogics: core.IParam.LogicStruct[] = [];
routerLogics.push(await logicCompoundV2Claim.build({ owner: owner.address, output }));
routerLogics.push(await compoundV2ClaimLogic.build({ owner: owner.address, output }));

// 4. send router tx
const transactionRequest = core.newRouterExecuteTransactionRequest({ chainId, routerLogics });
Expand Down
6 changes: 3 additions & 3 deletions test/logics/compound-v2/repay.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ describe('Test CompoundV2 Repay Logic', function () {

// 2. get borrow balance after 1000 blocks
await hrehelpers.mine(1000);
const logicCompoundV2Repay = new compoundv2.RepayLogic(chainId, hre.ethers.provider);
const { input } = await logicCompoundV2Repay.quote({ borrower: user.address, tokenIn: borrow.token });
const compoundV2RepayLogic = new compoundv2.RepayLogic(chainId, hre.ethers.provider);
const { input } = await compoundV2RepayLogic.quote({ borrower: user.address, tokenIn: borrow.token });
expect(input.amountWei).to.be.gt(borrow.amountWei);

// 3. build input, funds, tokensReturn
Expand All @@ -67,7 +67,7 @@ describe('Test CompoundV2 Repay Logic', function () {
// 4. build router logics
const erc20Funds = funds.erc20;
const routerLogics = await utils.getPermitAndPullTokenRouterLogics(chainId, user, erc20Funds);
routerLogics.push(await logicCompoundV2Repay.build({ input, balanceBps, borrower: user.address }));
routerLogics.push(await compoundV2RepayLogic.build({ input, balanceBps, borrower: user.address }));

// 5. send router tx
const transactionRequest = core.newRouterExecuteTransactionRequest({
Expand Down
Loading

0 comments on commit 874029a

Please sign in to comment.