diff --git a/packages/contract-helpers/src/commons/types.ts b/packages/contract-helpers/src/commons/types.ts index 5ce69546..0c12eff7 100644 --- a/packages/contract-helpers/src/commons/types.ts +++ b/packages/contract-helpers/src/commons/types.ts @@ -351,6 +351,12 @@ export type BorrowTxBuilder = { useOptimizedPath, encodedTxData, }: LPBorrowParamsType) => PopulatedTransaction; + encodeBorrowParams: ({ + reserve, + amount, + interestRateMode, + referralCode, + }: Omit) => Promise; }; export type RepayTxBuilder = { @@ -358,8 +364,28 @@ export type RepayTxBuilder = { generateSignedTxData: ( params: LPSignedRepayParamsType, ) => PopulatedTransaction; + encodeRepayParams: ({ + reserve, + amount, + interestRateMode, + }: Omit) => Promise; + encodeRepayWithPermitParams: ({ + reserve, + amount, + interestRateMode, + deadline, + signature, + }: Pick< + LPSignedRepayParamsType, + 'reserve' | 'amount' | 'interestRateMode' | 'signature' | 'deadline' + >) => Promise<[string, string, string]>; }; export type RepayWithATokensTxBuilder = { generateTxData: (params: LPRepayWithATokensType) => PopulatedTransaction; + encodeRepayWithATokensParams: ({ + reserve, + amount, + rateMode, + }: Omit) => Promise; }; diff --git a/packages/contract-helpers/src/lendingPool-contract-bundle/index.ts b/packages/contract-helpers/src/lendingPool-contract-bundle/index.ts index 5ce2be98..e3a19f88 100644 --- a/packages/contract-helpers/src/lendingPool-contract-bundle/index.ts +++ b/packages/contract-helpers/src/lendingPool-contract-bundle/index.ts @@ -5,6 +5,7 @@ import { InterestRate, LendingPoolMarketConfig, ProtocolAction, + RepayTxBuilder, tEthereumAddress, } from '../commons/types'; import { @@ -20,7 +21,6 @@ import { import { LPBorrowParamsType, LPDepositParamsType, - LPRepayParamsType, } from '../lendingPool-contract/lendingPoolTypes'; import { ILendingPool, @@ -44,12 +44,10 @@ export type DepositTxBuilder = { getApprovedAmount: ({ user, token }: TokenOwner) => Promise; }; -export type LPRepayTxBuilder = { - generateTxData: (args: LPRepayParamsType) => PopulatedTransaction; -}; - export interface LendingPoolBundleInterface { depositTxBuilder: DepositTxBuilder; + borrowTxBuilder: Pick; + repayTxBuilder: Pick; } export class LendingPoolBundle @@ -69,8 +67,8 @@ export class LendingPoolBundle readonly wethGatewayAddress: tEthereumAddress; depositTxBuilder: DepositTxBuilder; - borrowTxBuilder: Omit; - repayTxBuilder: LPRepayTxBuilder; + borrowTxBuilder: Pick; + repayTxBuilder: Pick; constructor( provider: providers.Provider, diff --git a/packages/contract-helpers/src/v3-pool-contract-bundle/index.ts b/packages/contract-helpers/src/v3-pool-contract-bundle/index.ts index 891246d3..2dfd3c82 100644 --- a/packages/contract-helpers/src/v3-pool-contract-bundle/index.ts +++ b/packages/contract-helpers/src/v3-pool-contract-bundle/index.ts @@ -34,10 +34,6 @@ import { import { IPool, IPoolInterface } from '../v3-pool-contract/typechain/IPool'; import { IPool__factory } from '../v3-pool-contract/typechain/IPool__factory'; import { L2Pool, L2PoolInterface } from '../v3-pool-rollups'; -import { - LPSupplyWithPermitType as LPSupplyWithPermitTypeL2, - LPBorrowParamsType as LPBorrowParamsTypeL2, -} from '../v3-pool-rollups/poolTypes'; import { WETHGatewayInterface, WETHGatewayService, @@ -64,6 +60,23 @@ export type SupplyTxBuilder = { encodedTxData, }: LPSignedSupplyParamsType) => PopulatedTransaction; getApprovedAmount: ({ user, token }: TokenOwner) => Promise; + encodeSupplyParams: ({ + reserve, + amount, + referralCode, + }: Pick< + LPSupplyParamsType, + 'reserve' | 'amount' | 'referralCode' + >) => Promise; + encodeSupplyWithPermitParams: ({ + reserve, + amount, + referralCode, + signature, + }: Pick< + LPSignedSupplyParamsType, + 'reserve' | 'amount' | 'referralCode' | 'signature' | 'deadline' + >) => Promise<[string, string, string]>; }; export interface PoolBundleInterface { @@ -156,6 +169,13 @@ export class PoolBundle useOptimizedPath, encodedTxData, }: LPSupplyParamsType): PopulatedTransaction => { + if (useOptimizedPath && encodedTxData) { + return this.l2PoolService.generateEncodedSupplyTxData({ + encodedTxData, + user, + }); + } + let actionTx: PopulatedTransaction = {}; const onBehalfOfParam = onBehalfOf ?? user; const referralCodeParam = referralCode ?? '0'; @@ -167,22 +187,6 @@ export class PoolBundle onBehalfOf: onBehalfOfParam, referralCode: referralCodeParam, }); - } else if (useOptimizedPath) { - if (encodedTxData) { - actionTx = this.l2PoolService.generateEncodedSupplyTxData({ - encodedTxData, - user, - }); - } else { - const args: LPSupplyParamsType = { - user, - reserve, - amount, - onBehalfOf: onBehalfOfParam, - referralCode: referralCodeParam, - }; - actionTx = this.l2PoolService.generateSupplyTxData(args); - } } else { const txData = this.contractInterface.encodeFunctionData('supply', [ reserve, @@ -211,58 +215,69 @@ export class PoolBundle deadline, encodedTxData, }: LPSignedSupplyParamsType): PopulatedTransaction => { + if (useOptimizedPath && encodedTxData) { + return this.l2PoolService.generateEncodedSupplyWithPermitTxData({ + encodedTxData, + user, + signature, + }); + } + const decomposedSignature: Signature = splitSignature(signature); - let populatedTx: PopulatedTransaction = {}; + const populatedTx: PopulatedTransaction = {}; const onBehalfOfParam = onBehalfOf ?? user; const referralCodeParam = referralCode ?? '0'; - if (useOptimizedPath) { - if (encodedTxData) { - populatedTx = - this.l2PoolService.generateEncodedSupplyWithPermitTxData({ - encodedTxData, - user, - signature, - }); - } else { - const args: LPSupplyWithPermitTypeL2 = { - user, - reserve, - amount, - referralCode: referralCodeParam, - onBehalfOf: onBehalfOfParam, - permitR: decomposedSignature.r, - permitS: decomposedSignature.s, - permitV: decomposedSignature.v, - deadline: Number(deadline), - }; - populatedTx = - this.l2PoolService.generateSupplyWithPermitTxData(args); - } - } else { - const txData = this.contractInterface.encodeFunctionData( - 'supplyWithPermit', - [ - reserve, - amount, - onBehalfOfParam, - referralCodeParam, - deadline, - decomposedSignature.v, - decomposedSignature.r, - decomposedSignature.s, - ], - ); - populatedTx.to = this.poolAddress; - populatedTx.from = user; - populatedTx.data = txData; - populatedTx.gasLimit = BigNumber.from( - gasLimitRecommendations[ProtocolAction.supplyWithPermit] - .recommended, - ); - } + const txData = this.contractInterface.encodeFunctionData( + 'supplyWithPermit', + [ + reserve, + amount, + onBehalfOfParam, + referralCodeParam, + deadline, + decomposedSignature.v, + decomposedSignature.r, + decomposedSignature.s, + ], + ); + populatedTx.to = this.poolAddress; + populatedTx.from = user; + populatedTx.data = txData; + populatedTx.gasLimit = BigNumber.from( + gasLimitRecommendations[ProtocolAction.supplyWithPermit].recommended, + ); return populatedTx; }, + encodeSupplyParams: async ({ + reserve, + amount, + referralCode, + }): Promise => { + return this.l2PoolService + .getEncoder() + .encodeSupplyParams(reserve, amount, referralCode ?? '0'); + }, + encodeSupplyWithPermitParams: async ({ + reserve, + amount, + signature, + deadline, + referralCode, + }): Promise<[string, string, string]> => { + const decomposedSignature: Signature = splitSignature(signature); + return this.l2PoolService + .getEncoder() + .encodeSupplyWithPermitParams( + reserve, + amount, + referralCode ?? '0', + deadline, + decomposedSignature.v, + decomposedSignature.r, + decomposedSignature.s, + ); + }, }; this.borrowTxBuilder = { @@ -277,6 +292,13 @@ export class PoolBundle useOptimizedPath, encodedTxData, }: LPBorrowParamsType): PopulatedTransaction => { + if (useOptimizedPath && encodedTxData) { + return this.l2PoolService.generateEncodedBorrowTxData({ + encodedTxData, + user, + }); + } + let actionTx: PopulatedTransaction = {}; const referralCodeParam = referralCode ?? '0'; const onBehalfOfParam = onBehalfOf ?? user; @@ -297,23 +319,6 @@ export class PoolBundle interestRateMode, referralCode: referralCodeParam, }); - } else if (useOptimizedPath) { - if (encodedTxData) { - actionTx = this.l2PoolService.generateEncodedBorrowTxData({ - encodedTxData, - user, - }); - } else { - const args: LPBorrowParamsTypeL2 = { - user, - reserve, - amount, - onBehalfOf: onBehalfOfParam, - referralCode: referralCodeParam, - numericRateMode, - }; - actionTx = this.l2PoolService.generateBorrowTxData(args); - } } else { const txData = this.contractInterface.encodeFunctionData('borrow', [ reserve, @@ -332,6 +337,21 @@ export class PoolBundle return actionTx; }, + encodeBorrowParams: async ({ + reserve, + amount, + interestRateMode, + referralCode, + }): Promise => { + return this.l2PoolService + .getEncoder() + .encodeBorrowParams( + reserve, + amount, + interestRateMode, + referralCode ?? '0', + ); + }, }; this.repayTxBuilder = { @@ -424,6 +444,31 @@ export class PoolBundle ); return populatedTx; }, + encodeRepayParams: async ({ reserve, amount, interestRateMode }) => { + return this.l2PoolService + .getEncoder() + .encodeRepayParams(reserve, amount, interestRateMode); + }, + encodeRepayWithPermitParams: async ({ + reserve, + amount, + interestRateMode, + signature, + deadline, + }) => { + const decomposedSignature: Signature = splitSignature(signature); + return this.l2PoolService + .getEncoder() + .encodeRepayWithPermitParams( + reserve, + amount, + interestRateMode, + deadline, + decomposedSignature.v, + decomposedSignature.r, + decomposedSignature.s, + ); + }, }; this.repayWithATokensTxBuilder = { @@ -467,6 +512,11 @@ export class PoolBundle return actionTx; }, + encodeRepayWithATokensParams: async ({ reserve, amount, rateMode }) => { + return this.l2PoolService + .getEncoder() + .encodeRepayWithATokensParams(reserve, amount, rateMode); + }, }; } } diff --git a/packages/contract-helpers/src/v3-pool-contract-bundle/pool-bundle.test.ts b/packages/contract-helpers/src/v3-pool-contract-bundle/pool-bundle.test.ts index 302491a5..62182946 100644 --- a/packages/contract-helpers/src/v3-pool-contract-bundle/pool-bundle.test.ts +++ b/packages/contract-helpers/src/v3-pool-contract-bundle/pool-bundle.test.ts @@ -1,6 +1,8 @@ import { BigNumber, providers } from 'ethers'; import { InterestRate } from '../commons/types'; import { API_ETH_MOCK_ADDRESS } from '../commons/utils'; +import { L2Encoder } from '../v3-pool-rollups/typechain/L2Encoder'; +import { L2Encoder__factory } from '../v3-pool-rollups/typechain/L2Encoder__factory'; import { PoolBundle } from './index'; jest.mock('../commons/gasStation', () => { @@ -28,6 +30,31 @@ describe('PoolBundle', () => { '0x0000000000000000000000000000000000000004'; const SWAP_COLLATERAL_ADAPTER = '0x0000000000000000000000000000000000000005'; const L2_ENCODER = '0x0000000000000000000000000000000000000020'; + + const encodedArg = + '0x0000000000000000000000000000000000000000000000000000006d6168616d'; + const permitR = + '0x0000000000000000000000000000000000000000000000000000000000000000'; + const permitS = + '0x0000000000000000000000000000000000000000000000000000000000000000'; + + const encoderSpy = jest.spyOn(L2Encoder__factory, 'connect').mockReturnValue({ + encodeSupplyParams: async () => Promise.resolve(encodedArg), + encodeSupplyWithPermitParams: async () => + Promise.resolve([encodedArg, permitR, permitS]), + encodeWithdrawParams: async () => Promise.resolve(encodedArg), + encodeBorrowParams: async () => Promise.resolve(encodedArg), + encodeRepayParams: async () => Promise.resolve(encodedArg), + encodeRepayWithPermitParams: async () => + Promise.resolve([encodedArg, permitR, permitS]), + encodeRepayWithATokensParams: async () => Promise.resolve(encodedArg), + encodeSwapBorrowRateMode: async () => Promise.resolve(encodedArg), + encodeSetUserUseReserveAsCollateral: async () => + Promise.resolve(encodedArg), + encodeLiquidationCall: async () => + Promise.resolve([encodedArg, encodedArg]), + } as unknown as L2Encoder); + describe('Initialization', () => { const config = { POOL, @@ -326,6 +353,46 @@ describe('PoolBundle', () => { '0x680dd47c0000000000000000000000000000000000000000000000000000006d6168616d532f8df4e2502bd869fb35e9301156f9b307380afdcc25cfbc87b2e939f16f7e47c326dc26eb918d327358797ee67ad7415d871ef7eaf0d4f6352d3ad021fbb4', ); }); + + it('encodes supply params for L2', async () => { + await instance.supplyTxBuilder.encodeSupplyParams({ + reserve: TOKEN, + amount: '1', + referralCode: '1', + }); + expect(encoderSpy).toHaveBeenCalled(); + }); + + it('encodes supply params for L2 without referral code', async () => { + await instance.supplyTxBuilder.encodeSupplyParams({ + reserve: TOKEN, + amount: '1', + }); + expect(encoderSpy).toHaveBeenCalled(); + }); + + it('encodes supply with permit parmas for L2', async () => { + await instance.supplyTxBuilder.encodeSupplyWithPermitParams({ + reserve: TOKEN, + amount: '1', + referralCode: '1', + deadline: '10000', + signature: + '0x532f8df4e2502bd869fb35e9301156f9b307380afdcc25cfbc87b2e939f16f7e47c326dc26eb918d327358797ee67ad7415d871ef7eaf0d4f6352d3ad021fbb41c', + }); + expect(encoderSpy).toHaveBeenCalled(); + }); + + it('encodes supply with permit parmas for L2 without referral code', async () => { + await instance.supplyTxBuilder.encodeSupplyWithPermitParams({ + reserve: TOKEN, + amount: '1', + deadline: '10000', + signature: + '0x532f8df4e2502bd869fb35e9301156f9b307380afdcc25cfbc87b2e939f16f7e47c326dc26eb918d327358797ee67ad7415d871ef7eaf0d4f6352d3ad021fbb41c', + }); + expect(encoderSpy).toHaveBeenCalled(); + }); }); describe('BorrowTxBuilder', () => { @@ -523,6 +590,25 @@ describe('PoolBundle', () => { // Will be identical to variable, since tx data is pre-encoded, rate mode has no effect expect(resultStable.data).toEqual(txData); }); + + it('encodes borrow params for L2', async () => { + await instance.borrowTxBuilder.encodeBorrowParams({ + reserve: TOKEN, + amount: '1', + interestRateMode: InterestRate.Variable, + referralCode: '1', + }); + expect(encoderSpy).toHaveBeenCalled(); + }); + + it('encodes borrow params for L2 without referral code', async () => { + await instance.borrowTxBuilder.encodeBorrowParams({ + reserve: TOKEN, + amount: '1', + interestRateMode: InterestRate.Variable, + }); + expect(encoderSpy).toHaveBeenCalled(); + }); }); describe('RepayTxBuilder', () => { @@ -737,6 +823,27 @@ describe('PoolBundle', () => { '0xee3e210b00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000000000001c532f8df4e2502bd869fb35e9301156f9b307380afdcc25cfbc87b2e939f16f7e47c326dc26eb918d327358797ee67ad7415d871ef7eaf0d4f6352d3ad021fbb4', ); }); + + it('encodes repay params for L2', async () => { + await instance.repayTxBuilder.encodeRepayParams({ + reserve: TOKEN, + amount: '1', + interestRateMode: InterestRate.Variable, + }); + expect(encoderSpy).toHaveBeenCalled(); + }); + + it('encodes repay with permit params for L2', async () => { + await instance.repayTxBuilder.encodeRepayWithPermitParams({ + reserve: TOKEN, + amount: '1', + interestRateMode: InterestRate.Variable, + deadline: '10000', + signature: + '0x532f8df4e2502bd869fb35e9301156f9b307380afdcc25cfbc87b2e939f16f7e47c326dc26eb918d327358797ee67ad7415d871ef7eaf0d4f6352d3ad021fbb41c', + }); + expect(encoderSpy).toHaveBeenCalled(); + }); }); describe('RepayWithATokenTxBuilder', () => { @@ -825,5 +932,14 @@ describe('PoolBundle', () => { '0xdc7c0bff0000000000000000000000000000000000000000000000000000006d6168616d', ); }); + + it('encodes repay with aToken params for L2', async () => { + await instance.repayWithATokensTxBuilder.encodeRepayWithATokensParams({ + reserve: TOKEN, + amount: '1', + rateMode: InterestRate.Variable, + }); + expect(encoderSpy).toHaveBeenCalled(); + }); }); });