From 2bcb71eadcec8ce5fcad6d5cf0303c0506fb3262 Mon Sep 17 00:00:00 2001 From: ChouAndy Date: Thu, 3 Aug 2023 13:00:39 +0800 Subject: [PATCH] fix: fix for pr comments --- src/logics/aave-v2/logic.flash-loan.ts | 4 +- src/logics/aave-v2/service.test.ts | 2 +- src/logics/aave-v2/service.ts | 4 +- src/logics/aave-v2/types.ts | 2 +- src/logics/aave-v3/logic.flash-loan.ts | 4 +- src/logics/aave-v3/service.test.ts | 2 +- src/logics/aave-v3/service.ts | 4 +- src/logics/aave-v3/types.ts | 2 +- .../abis/ProtocolFeesCollector.json | 15 +++++ src/logics/balancer-v2/configs.ts | 14 +++- .../contracts/ProtocolFeesCollector.ts | 64 +++++++++++++++++++ .../ProtocolFeesCollector__factory.ts | 33 ++++++++++ .../balancer-v2/contracts/factories/index.ts | 1 + src/logics/balancer-v2/contracts/index.ts | 2 + src/logics/balancer-v2/logic.flash-loan.ts | 48 ++++++++++---- 15 files changed, 176 insertions(+), 25 deletions(-) create mode 100644 src/logics/balancer-v2/abis/ProtocolFeesCollector.json create mode 100644 src/logics/balancer-v2/contracts/ProtocolFeesCollector.ts create mode 100644 src/logics/balancer-v2/contracts/factories/ProtocolFeesCollector__factory.ts diff --git a/src/logics/aave-v2/logic.flash-loan.ts b/src/logics/aave-v2/logic.flash-loan.ts index a405f5e2..2dc83f7b 100644 --- a/src/logics/aave-v2/logic.flash-loan.ts +++ b/src/logics/aave-v2/logic.flash-loan.ts @@ -53,9 +53,9 @@ export class FlashLoanLogic extends core.Logic implements core.LogicTokenListInt const fees = new common.TokenAmounts(); for (let i = 0; i < loans.length; i++) { const loan = loans.at(i); - const { isActive, avaliableToBorrow } = assetInfos[i]; + const { isActive, availableToBorrow } = assetInfos[i]; invariant(isActive, `asset is not active: ${loan.token.address}`); - invariant(avaliableToBorrow.gte(loan), `insufficient borrowing capacity for the asset: ${loan.token.address}`); + invariant(availableToBorrow.gte(loan), `insufficient borrowing capacity for the asset: ${loan.token.address}`); const feeAmountWei = common.calcFee(loan.amountWei, feeBps); const fee = new common.TokenAmount(loan.token).setWei(feeAmountWei); diff --git a/src/logics/aave-v2/service.test.ts b/src/logics/aave-v2/service.test.ts index cd96bb11..598e985d 100644 --- a/src/logics/aave-v2/service.test.ts +++ b/src/logics/aave-v2/service.test.ts @@ -62,7 +62,7 @@ describe('AaveV2 Service', function () { expect(flashLoanConfiguration).to.have.keys('feeBps', 'assetInfos'); expect(flashLoanConfiguration.assetInfos).to.have.lengthOf.above(0); for (const assetInfo of flashLoanConfiguration.assetInfos) { - expect(assetInfo).to.have.keys('isActive', 'avaliableToBorrow'); + expect(assetInfo).to.have.keys('isActive', 'availableToBorrow'); } }); }); diff --git a/src/logics/aave-v2/service.ts b/src/logics/aave-v2/service.ts index 3373baf6..e2938b42 100644 --- a/src/logics/aave-v2/service.ts +++ b/src/logics/aave-v2/service.ts @@ -279,10 +279,10 @@ export class Service extends common.Web3Toolkit { j++; const [balance] = this.erc20Iface.decodeFunctionResult('balanceOf', returnData[j]); - const avaliableToBorrow = new common.TokenAmount(assets[i]).setWei(balance); + const availableToBorrow = new common.TokenAmount(assets[i]).setWei(balance); j++; - assetInfos.push({ isActive, avaliableToBorrow }); + assetInfos.push({ isActive, availableToBorrow }); } return { feeBps: feeBps, assetInfos }; diff --git a/src/logics/aave-v2/types.ts b/src/logics/aave-v2/types.ts index 93d91134..3583e875 100644 --- a/src/logics/aave-v2/types.ts +++ b/src/logics/aave-v2/types.ts @@ -22,7 +22,7 @@ export enum InterestRateMode { export interface FlashLoanAssetInfo { isActive: boolean; - avaliableToBorrow: common.TokenAmount; + availableToBorrow: common.TokenAmount; } export interface FlashLoanConfiguration { diff --git a/src/logics/aave-v3/logic.flash-loan.ts b/src/logics/aave-v3/logic.flash-loan.ts index 3fe9ee14..5432c301 100644 --- a/src/logics/aave-v3/logic.flash-loan.ts +++ b/src/logics/aave-v3/logic.flash-loan.ts @@ -56,11 +56,11 @@ export class FlashLoanLogic const fees = new common.TokenAmounts(); for (let i = 0; i < loans.length; i++) { const loan = loans.at(i); - const { isActive, isPaused, isFlashLoanEnabled, avaliableToBorrow } = assetInfos[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(avaliableToBorrow.gte(loan), `insufficient borrowing capacity for the asset: ${loan.token.address}`); + invariant(availableToBorrow.gte(loan), `insufficient borrowing capacity for the asset: ${loan.token.address}`); const feeAmountWei = common.calcFee(loan.amountWei, feeBps); const fee = new common.TokenAmount(loan.token).setWei(feeAmountWei); diff --git a/src/logics/aave-v3/service.test.ts b/src/logics/aave-v3/service.test.ts index 836047fe..32dc07bd 100644 --- a/src/logics/aave-v3/service.test.ts +++ b/src/logics/aave-v3/service.test.ts @@ -62,7 +62,7 @@ describe('AaveV3 Service', function () { expect(flashLoanConfiguration).to.have.keys('feeBps', 'assetInfos'); expect(flashLoanConfiguration.assetInfos).to.have.lengthOf.above(0); for (const assetInfo of flashLoanConfiguration.assetInfos) { - expect(assetInfo).to.have.keys('isPaused', 'isActive', 'isFlashLoanEnabled', 'avaliableToBorrow'); + expect(assetInfo).to.have.keys('isPaused', 'isActive', 'isFlashLoanEnabled', 'availableToBorrow'); } }); }); diff --git a/src/logics/aave-v3/service.ts b/src/logics/aave-v3/service.ts index 84118570..d8e57dde 100644 --- a/src/logics/aave-v3/service.ts +++ b/src/logics/aave-v3/service.ts @@ -296,10 +296,10 @@ export class Service extends common.Web3Toolkit { j++; const [balance] = this.erc20Iface.decodeFunctionResult('balanceOf', returnData[j]); - const avaliableToBorrow = new common.TokenAmount(assets[i]).setWei(balance); + const availableToBorrow = new common.TokenAmount(assets[i]).setWei(balance); j++; - assetInfos.push({ isPaused, isActive, isFlashLoanEnabled, avaliableToBorrow }); + assetInfos.push({ isPaused, isActive, isFlashLoanEnabled, availableToBorrow }); } return { feeBps: feeBps, assetInfos }; diff --git a/src/logics/aave-v3/types.ts b/src/logics/aave-v3/types.ts index 75e5cc8f..4408ccce 100644 --- a/src/logics/aave-v3/types.ts +++ b/src/logics/aave-v3/types.ts @@ -24,7 +24,7 @@ export interface FlashLoanAssetInfo { isPaused: boolean; isActive: boolean; isFlashLoanEnabled: boolean; - avaliableToBorrow: common.TokenAmount; + availableToBorrow: common.TokenAmount; } export interface FlashLoanConfiguration { diff --git a/src/logics/balancer-v2/abis/ProtocolFeesCollector.json b/src/logics/balancer-v2/abis/ProtocolFeesCollector.json new file mode 100644 index 00000000..7a039c3b --- /dev/null +++ b/src/logics/balancer-v2/abis/ProtocolFeesCollector.json @@ -0,0 +1,15 @@ +[ + { + "inputs": [], + "name": "getFlashLoanFeePercentage", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/logics/balancer-v2/configs.ts b/src/logics/balancer-v2/configs.ts index 28b0fcf7..a6a53ebb 100644 --- a/src/logics/balancer-v2/configs.ts +++ b/src/logics/balancer-v2/configs.ts @@ -1,6 +1,6 @@ import * as common from '@protocolink/common'; -type ContractNames = 'Vault' | 'BalancerV2FlashLoanCallback'; +type ContractNames = 'Vault' | 'BalancerV2FlashLoanCallback' | 'ProtocolFeesCollector'; export interface Config { chainId: number; @@ -13,6 +13,7 @@ export const configs: Config[] = [ contract: { Vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', BalancerV2FlashLoanCallback: '0x03EdE4da71B8c404f7a2d61b8Ad367edEFc90Af8', + ProtocolFeesCollector: '0xce88686553686DA562CE7Cea497CE749DA109f9F', }, }, { @@ -20,6 +21,7 @@ export const configs: Config[] = [ contract: { Vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', BalancerV2FlashLoanCallback: '0x03EdE4da71B8c404f7a2d61b8Ad367edEFc90Af8', + ProtocolFeesCollector: '0xce88686553686DA562CE7Cea497CE749DA109f9F', }, }, { @@ -27,6 +29,7 @@ export const configs: Config[] = [ contract: { Vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', BalancerV2FlashLoanCallback: '0x03EdE4da71B8c404f7a2d61b8Ad367edEFc90Af8', + ProtocolFeesCollector: '0xce88686553686DA562CE7Cea497CE749DA109f9F', }, }, { @@ -34,6 +37,15 @@ export const configs: Config[] = [ contract: { Vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', BalancerV2FlashLoanCallback: '0x03EdE4da71B8c404f7a2d61b8Ad367edEFc90Af8', + ProtocolFeesCollector: '0xce88686553686DA562CE7Cea497CE749DA109f9F', + }, + }, + { + chainId: common.ChainId.avalanche, + contract: { + Vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', + BalancerV2FlashLoanCallback: '0x03EdE4da71B8c404f7a2d61b8Ad367edEFc90Af8', + ProtocolFeesCollector: '0xce88686553686DA562CE7Cea497CE749DA109f9F', }, }, ]; diff --git a/src/logics/balancer-v2/contracts/ProtocolFeesCollector.ts b/src/logics/balancer-v2/contracts/ProtocolFeesCollector.ts new file mode 100644 index 00000000..c5fd98e3 --- /dev/null +++ b/src/logics/balancer-v2/contracts/ProtocolFeesCollector.ts @@ -0,0 +1,64 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import type { BaseContract, BigNumber, BytesLike, CallOverrides, PopulatedTransaction, Signer, utils } from 'ethers'; +import type { FunctionFragment, Result } from '@ethersproject/abi'; +import type { Listener, Provider } from '@ethersproject/providers'; +import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent } from './common'; + +export interface ProtocolFeesCollectorInterface extends utils.Interface { + functions: { + 'getFlashLoanFeePercentage()': FunctionFragment; + }; + + getFunction(nameOrSignatureOrTopic: 'getFlashLoanFeePercentage'): FunctionFragment; + + encodeFunctionData(functionFragment: 'getFlashLoanFeePercentage', values?: undefined): string; + + decodeFunctionResult(functionFragment: 'getFlashLoanFeePercentage', data: BytesLike): Result; + + events: {}; +} + +export interface ProtocolFeesCollector extends BaseContract { + connect(signerOrProvider: Signer | Provider | string): this; + attach(addressOrName: string): this; + deployed(): Promise; + + interface: ProtocolFeesCollectorInterface; + + queryFilter( + event: TypedEventFilter, + fromBlockOrBlockhash?: string | number | undefined, + toBlock?: string | number | undefined + ): Promise>; + + listeners(eventFilter?: TypedEventFilter): Array>; + listeners(eventName?: string): Array; + removeAllListeners(eventFilter: TypedEventFilter): this; + removeAllListeners(eventName?: string): this; + off: OnEvent; + on: OnEvent; + once: OnEvent; + removeListener: OnEvent; + + functions: { + getFlashLoanFeePercentage(overrides?: CallOverrides): Promise<[BigNumber]>; + }; + + getFlashLoanFeePercentage(overrides?: CallOverrides): Promise; + + callStatic: { + getFlashLoanFeePercentage(overrides?: CallOverrides): Promise; + }; + + filters: {}; + + estimateGas: { + getFlashLoanFeePercentage(overrides?: CallOverrides): Promise; + }; + + populateTransaction: { + getFlashLoanFeePercentage(overrides?: CallOverrides): Promise; + }; +} diff --git a/src/logics/balancer-v2/contracts/factories/ProtocolFeesCollector__factory.ts b/src/logics/balancer-v2/contracts/factories/ProtocolFeesCollector__factory.ts new file mode 100644 index 00000000..8463f260 --- /dev/null +++ b/src/logics/balancer-v2/contracts/factories/ProtocolFeesCollector__factory.ts @@ -0,0 +1,33 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ + +import { Contract, Signer, utils } from 'ethers'; +import type { Provider } from '@ethersproject/providers'; +import type { ProtocolFeesCollector, ProtocolFeesCollectorInterface } from '../ProtocolFeesCollector'; + +const _abi = [ + { + inputs: [], + name: 'getFlashLoanFeePercentage', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const; + +export class ProtocolFeesCollector__factory { + static readonly abi = _abi; + static createInterface(): ProtocolFeesCollectorInterface { + return new utils.Interface(_abi) as ProtocolFeesCollectorInterface; + } + static connect(address: string, signerOrProvider: Signer | Provider): ProtocolFeesCollector { + return new Contract(address, _abi, signerOrProvider) as ProtocolFeesCollector; + } +} diff --git a/src/logics/balancer-v2/contracts/factories/index.ts b/src/logics/balancer-v2/contracts/factories/index.ts index c99b6987..8e40173d 100644 --- a/src/logics/balancer-v2/contracts/factories/index.ts +++ b/src/logics/balancer-v2/contracts/factories/index.ts @@ -2,4 +2,5 @@ /* tslint:disable */ /* eslint-disable */ export { BalancerV2FlashLoanCallback__factory } from './BalancerV2FlashLoanCallback__factory'; +export { ProtocolFeesCollector__factory } from './ProtocolFeesCollector__factory'; export { Vault__factory } from './Vault__factory'; diff --git a/src/logics/balancer-v2/contracts/index.ts b/src/logics/balancer-v2/contracts/index.ts index 14a950aa..df8ee904 100644 --- a/src/logics/balancer-v2/contracts/index.ts +++ b/src/logics/balancer-v2/contracts/index.ts @@ -2,7 +2,9 @@ /* tslint:disable */ /* eslint-disable */ export type { BalancerV2FlashLoanCallback } from './BalancerV2FlashLoanCallback'; +export type { ProtocolFeesCollector } from './ProtocolFeesCollector'; export type { Vault } from './Vault'; export * as factories from './factories'; export { BalancerV2FlashLoanCallback__factory } from './factories/BalancerV2FlashLoanCallback__factory'; +export { ProtocolFeesCollector__factory } from './factories/ProtocolFeesCollector__factory'; export { Vault__factory } from './factories/Vault__factory'; diff --git a/src/logics/balancer-v2/logic.flash-loan.ts b/src/logics/balancer-v2/logic.flash-loan.ts index 054f885d..8884cf5b 100644 --- a/src/logics/balancer-v2/logic.flash-loan.ts +++ b/src/logics/balancer-v2/logic.flash-loan.ts @@ -1,6 +1,6 @@ import { BigNumberish } from 'ethers'; +import { ProtocolFeesCollector__factory, Vault__factory } from './contracts'; import { TokenList } from '@uniswap/token-lists'; -import { Vault__factory } from './contracts'; import { axios } from 'src/utils'; import * as common from '@protocolink/common'; import * as core from '@protocolink/core'; @@ -30,7 +30,7 @@ export class FlashLoanLogic extends core.Logic implements core.LogicTokenListInt async getTokenList() { const { data } = await axios.get( - 'https://raw.githubusercontent.com/balancer/tokenlists/main/generated/listed-old.tokenlist.json' + 'https://raw.githubusercontent.com/balancer/tokenlists/main/generated/balancer.tokenlist.json' ); const tmp: Record = {}; @@ -46,26 +46,50 @@ export class FlashLoanLogic extends core.Logic implements core.LogicTokenListInt async quote(params: FlashLoanLogicParams) { const { outputs: loans } = params; + invariant(new Set(loans.map(({ token }) => token.address)).size === loans.length, 'loans have duplicate tokens'); 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 protocolFeesCollectorIface = ProtocolFeesCollector__factory.createInterface(); + + const calls: common.Multicall2.CallStruct[] = [ + { + target: getContractAddress(this.chainId, 'ProtocolFeesCollector'), + callData: protocolFeesCollectorIface.encodeFunctionData('getFlashLoanFeePercentage'), + }, + ]; + loans.forEach(({ token }) => { + calls.push({ + target: token.address, + callData: this.erc20Iface.encodeFunctionData('balanceOf', [vaultAddress]), + }); + }); const { returnData } = await this.multicall2.callStatic.aggregate(calls); + let j = 0; + const [flashLoanFeePercentage] = protocolFeesCollectorIface.decodeFunctionResult( + 'getFlashLoanFeePercentage', + returnData[j] + ); + const feeBps = flashLoanFeePercentage.toNumber(); + j++; + 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}`); + const [balance] = this.erc20Iface.decodeFunctionResult('balanceOf', returnData[j]); + const availableToBorrow = new common.TokenAmount(loan.token).setWei(balance); + invariant(availableToBorrow.gte(loan), `insufficient borrowing capacity for the asset: ${loan.token.address}`); + j++; + + const feeAmountWei = common.calcFee(loan.amountWei, feeBps); + const fee = new common.TokenAmount(loan.token).setWei(feeAmountWei); + fees.add(fee); - repays.add(loan.token, loan.amount); - fees.add(loan.token, '0'); + const repay = loan.clone().add(fee); + repays.add(repay); } - const quotation: FlashLoanLogicQuotation = { loans, repays, fees, feeBps: 0 }; + const quotation: FlashLoanLogicQuotation = { loans, repays, fees, feeBps }; return quotation; }