From c8441e739654355713d3d98bf37b49abb182f464 Mon Sep 17 00:00:00 2001 From: Joaquin Battilana Date: Tue, 8 Oct 2024 18:22:10 +0100 Subject: [PATCH 1/4] feat: new weth gateway and tests --- .../src/wethgateway-contract/index.ts | 48 +- .../typechain/IWETHGateway.d.ts | 381 ------------- .../typechain/IWETHGateway__factory.ts | 124 ---- .../typechain/WrappedTokenGatewayV3.ts | 528 ++++++++++++++++++ .../WrappedTokenGatewayV3__factory.ts | 299 ++++++++++ .../wethgateway-contract/typechain/common.ts | 43 ++ .../wethgateway-contract/wethGateway.test.ts | 223 +------- 7 files changed, 904 insertions(+), 742 deletions(-) delete mode 100644 packages/contract-helpers/src/wethgateway-contract/typechain/IWETHGateway.d.ts delete mode 100644 packages/contract-helpers/src/wethgateway-contract/typechain/IWETHGateway__factory.ts create mode 100644 packages/contract-helpers/src/wethgateway-contract/typechain/WrappedTokenGatewayV3.ts create mode 100644 packages/contract-helpers/src/wethgateway-contract/typechain/WrappedTokenGatewayV3__factory.ts create mode 100644 packages/contract-helpers/src/wethgateway-contract/typechain/common.ts diff --git a/packages/contract-helpers/src/wethgateway-contract/index.ts b/packages/contract-helpers/src/wethgateway-contract/index.ts index d3c262c9c..3c2fd6da2 100644 --- a/packages/contract-helpers/src/wethgateway-contract/index.ts +++ b/packages/contract-helpers/src/wethgateway-contract/index.ts @@ -7,7 +7,6 @@ import BaseService from '../commons/BaseService'; import { eEthereumTxType, EthereumTransactionTypeExtended, - InterestRate, ProtocolAction, tEthereumAddress, transactionType, @@ -21,8 +20,11 @@ import { isPositiveOrMinusOneAmount, } from '../commons/validators/paramValidators'; import { IERC20ServiceInterface } from '../erc20-contract'; -import { IWETHGateway, IWETHGatewayInterface } from './typechain/IWETHGateway'; -import { IWETHGateway__factory } from './typechain/IWETHGateway__factory'; +import { + WrappedTokenGatewayV3, + WrappedTokenGatewayV3Interface, +} from './typechain/WrappedTokenGatewayV3'; +import { WrappedTokenGatewayV3__factory } from './typechain/WrappedTokenGatewayV3__factory'; export type WETHDepositParamsType = { lendingPool: tEthereumAddress; @@ -44,7 +46,6 @@ export type WETHRepayParamsType = { lendingPool: tEthereumAddress; user: tEthereumAddress; amount: string; - interestRateMode: InterestRate; onBehalfOf?: tEthereumAddress; }; @@ -53,7 +54,6 @@ export type WETHBorrowParamsType = { user: tEthereumAddress; amount: string; debtTokenAddress?: tEthereumAddress; - interestRateMode: InterestRate; referralCode?: string; }; @@ -76,7 +76,7 @@ export interface WETHGatewayInterface { } export class WETHGatewayService - extends BaseService + extends BaseService implements WETHGatewayInterface { readonly wethGatewayAddress: string; @@ -85,7 +85,7 @@ export class WETHGatewayService readonly erc20Service: IERC20ServiceInterface; - readonly wethGatewayInstance: IWETHGatewayInterface; + readonly wethGatewayInstance: WrappedTokenGatewayV3Interface; generateDepositEthTxData: ( args: WETHDepositParamsType, @@ -100,7 +100,7 @@ export class WETHGatewayService erc20Service: IERC20ServiceInterface, wethGatewayAddress?: string, ) { - super(provider, IWETHGateway__factory); + super(provider, WrappedTokenGatewayV3__factory); this.erc20Service = erc20Service; this.baseDebtTokenService = new BaseDebtToken( @@ -114,7 +114,7 @@ export class WETHGatewayService this.withdrawETH = this.withdrawETH.bind(this); this.repayETH = this.repayETH.bind(this); this.borrowETH = this.borrowETH.bind(this); - this.wethGatewayInstance = IWETHGateway__factory.createInterface(); + this.wethGatewayInstance = WrappedTokenGatewayV3__factory.createInterface(); this.generateDepositEthTxData = ( args: WETHDepositParamsType, ): PopulatedTransaction => { @@ -138,12 +138,9 @@ export class WETHGatewayService this.generateBorrowEthTxData = ( args: WETHBorrowParamsType, ): PopulatedTransaction => { - const numericRateMode = - args.interestRateMode === InterestRate.Variable ? 2 : 1; const txData = this.wethGatewayInstance.encodeFunctionData('borrowETH', [ args.lendingPool, args.amount, - numericRateMode, args.referralCode ?? '0', ]); const actionTx: PopulatedTransaction = { @@ -158,18 +155,14 @@ export class WETHGatewayService }; this.generateRepayEthTxData = ({ - interestRateMode, lendingPool, amount, user, onBehalfOf, }) => { - const numericRateMode = - interestRateMode === InterestRate.Variable ? 2 : 1; const txData = this.wethGatewayInstance.encodeFunctionData('repayETH', [ lendingPool, amount, - numericRateMode, onBehalfOf ?? user, ]); const actionTx: PopulatedTransaction = { @@ -202,7 +195,7 @@ export class WETHGatewayService ): EthereumTransactionTypeExtended[] { const convertedAmount: string = valueToWei(amount, 18); - const wethGatewayContract: IWETHGateway = this.getContractInstance( + const wethGatewayContract: WrappedTokenGatewayV3 = this.getContractInstance( this.wethGatewayAddress, ); const txCallback: () => Promise = this.generateTxCallback({ @@ -237,16 +230,14 @@ export class WETHGatewayService user, amount, debtTokenAddress, - interestRateMode, referralCode, }: WETHBorrowParamsType, ): Promise { const txs: EthereumTransactionTypeExtended[] = []; const convertedAmount: string = valueToWei(amount, 18); - const numericRateMode = interestRateMode === InterestRate.Variable ? 2 : 1; if (!debtTokenAddress) { throw new Error( - `To borrow ETH you need to pass the stable or variable WETH debt Token Address corresponding the interestRateMode`, + `To borrow ETH you need to pass the variable WETH debt Token Address`, ); } @@ -270,7 +261,7 @@ export class WETHGatewayService txs.push(approveDelegationTx); } - const wethGatewayContract: IWETHGateway = this.getContractInstance( + const wethGatewayContract: WrappedTokenGatewayV3 = this.getContractInstance( this.wethGatewayAddress, ); @@ -279,7 +270,6 @@ export class WETHGatewayService wethGatewayContract.populateTransaction.borrowETH( lendingPool, convertedAmount, - numericRateMode, referralCode ?? '0', ), from: user, @@ -337,7 +327,7 @@ export class WETHGatewayService txs.push(approveTx); } - const wethGatewayContract: IWETHGateway = this.getContractInstance( + const wethGatewayContract: WrappedTokenGatewayV3 = this.getContractInstance( this.wethGatewayAddress, ); @@ -370,17 +360,10 @@ export class WETHGatewayService @isEthAddress('user') @isEthAddress('onBehalfOf') @isPositiveAmount('amount') - { - lendingPool, - user, - amount, - interestRateMode, - onBehalfOf, - }: WETHRepayParamsType, + { lendingPool, user, amount, onBehalfOf }: WETHRepayParamsType, ): EthereumTransactionTypeExtended[] { const convertedAmount: string = valueToWei(amount, 18); - const numericRateMode = interestRateMode === InterestRate.Variable ? 2 : 1; - const wethGatewayContract: IWETHGateway = this.getContractInstance( + const wethGatewayContract: WrappedTokenGatewayV3 = this.getContractInstance( this.wethGatewayAddress, ); @@ -389,7 +372,6 @@ export class WETHGatewayService wethGatewayContract.populateTransaction.repayETH( lendingPool, convertedAmount, - numericRateMode, onBehalfOf ?? user, ), gasSurplus: 30, diff --git a/packages/contract-helpers/src/wethgateway-contract/typechain/IWETHGateway.d.ts b/packages/contract-helpers/src/wethgateway-contract/typechain/IWETHGateway.d.ts deleted file mode 100644 index 5940f4dff..000000000 --- a/packages/contract-helpers/src/wethgateway-contract/typechain/IWETHGateway.d.ts +++ /dev/null @@ -1,381 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* eslint-disable */ - -import { - ethers, - EventFilter, - Signer, - BigNumber, - BigNumberish, - PopulatedTransaction, -} from 'ethers'; -import { - Contract, - ContractTransaction, - Overrides, - PayableOverrides, - CallOverrides, -} from '@ethersproject/contracts'; -import { BytesLike } from '@ethersproject/bytes'; -import { Listener, Provider } from '@ethersproject/providers'; -import { FunctionFragment, EventFragment, Result } from '@ethersproject/abi'; - -interface IWETHGatewayInterface extends ethers.utils.Interface { - functions: { - 'borrowETH(address,uint256,uint256,uint16)': FunctionFragment; - 'depositETH(address,address,uint16)': FunctionFragment; - 'repayETH(address,uint256,uint256,address)': FunctionFragment; - 'withdrawETH(address,uint256,address)': FunctionFragment; - }; - - encodeFunctionData( - functionFragment: 'borrowETH', - values: [string, BigNumberish, BigNumberish, BigNumberish], - ): string; - encodeFunctionData( - functionFragment: 'depositETH', - values: [string, string, BigNumberish], - ): string; - encodeFunctionData( - functionFragment: 'repayETH', - values: [string, BigNumberish, BigNumberish, string], - ): string; - encodeFunctionData( - functionFragment: 'withdrawETH', - values: [string, BigNumberish, string], - ): string; - - decodeFunctionResult(functionFragment: 'borrowETH', data: BytesLike): Result; - decodeFunctionResult(functionFragment: 'depositETH', data: BytesLike): Result; - decodeFunctionResult(functionFragment: 'repayETH', data: BytesLike): Result; - decodeFunctionResult( - functionFragment: 'withdrawETH', - data: BytesLike, - ): Result; - - events: {}; -} - -export class IWETHGateway extends Contract { - connect(signerOrProvider: Signer | Provider | string): this; - attach(addressOrName: string): this; - deployed(): Promise; - - on(event: EventFilter | string, listener: Listener): this; - once(event: EventFilter | string, listener: Listener): this; - addListener(eventName: EventFilter | string, listener: Listener): this; - removeAllListeners(eventName: EventFilter | string): this; - removeListener(eventName: any, listener: Listener): this; - - interface: IWETHGatewayInterface; - - functions: { - borrowETH( - lendingPool: string, - amount: BigNumberish, - interesRateMode: BigNumberish, - referralCode: BigNumberish, - overrides?: Overrides, - ): Promise; - - 'borrowETH(address,uint256,uint256,uint16)'( - lendingPool: string, - amount: BigNumberish, - interesRateMode: BigNumberish, - referralCode: BigNumberish, - overrides?: Overrides, - ): Promise; - - depositETH( - lendingPool: string, - onBehalfOf: string, - referralCode: BigNumberish, - overrides?: PayableOverrides, - ): Promise; - - 'depositETH(address,address,uint16)'( - lendingPool: string, - onBehalfOf: string, - referralCode: BigNumberish, - overrides?: PayableOverrides, - ): Promise; - - repayETH( - lendingPool: string, - amount: BigNumberish, - rateMode: BigNumberish, - onBehalfOf: string, - overrides?: PayableOverrides, - ): Promise; - - 'repayETH(address,uint256,uint256,address)'( - lendingPool: string, - amount: BigNumberish, - rateMode: BigNumberish, - onBehalfOf: string, - overrides?: PayableOverrides, - ): Promise; - - withdrawETH( - lendingPool: string, - amount: BigNumberish, - onBehalfOf: string, - overrides?: Overrides, - ): Promise; - - 'withdrawETH(address,uint256,address)'( - lendingPool: string, - amount: BigNumberish, - onBehalfOf: string, - overrides?: Overrides, - ): Promise; - }; - - borrowETH( - lendingPool: string, - amount: BigNumberish, - interesRateMode: BigNumberish, - referralCode: BigNumberish, - overrides?: Overrides, - ): Promise; - - 'borrowETH(address,uint256,uint256,uint16)'( - lendingPool: string, - amount: BigNumberish, - interesRateMode: BigNumberish, - referralCode: BigNumberish, - overrides?: Overrides, - ): Promise; - - depositETH( - lendingPool: string, - onBehalfOf: string, - referralCode: BigNumberish, - overrides?: PayableOverrides, - ): Promise; - - 'depositETH(address,address,uint16)'( - lendingPool: string, - onBehalfOf: string, - referralCode: BigNumberish, - overrides?: PayableOverrides, - ): Promise; - - repayETH( - lendingPool: string, - amount: BigNumberish, - rateMode: BigNumberish, - onBehalfOf: string, - overrides?: PayableOverrides, - ): Promise; - - 'repayETH(address,uint256,uint256,address)'( - lendingPool: string, - amount: BigNumberish, - rateMode: BigNumberish, - onBehalfOf: string, - overrides?: PayableOverrides, - ): Promise; - - withdrawETH( - lendingPool: string, - amount: BigNumberish, - onBehalfOf: string, - overrides?: Overrides, - ): Promise; - - 'withdrawETH(address,uint256,address)'( - lendingPool: string, - amount: BigNumberish, - onBehalfOf: string, - overrides?: Overrides, - ): Promise; - - callStatic: { - borrowETH( - lendingPool: string, - amount: BigNumberish, - interesRateMode: BigNumberish, - referralCode: BigNumberish, - overrides?: CallOverrides, - ): Promise; - - 'borrowETH(address,uint256,uint256,uint16)'( - lendingPool: string, - amount: BigNumberish, - interesRateMode: BigNumberish, - referralCode: BigNumberish, - overrides?: CallOverrides, - ): Promise; - - depositETH( - lendingPool: string, - onBehalfOf: string, - referralCode: BigNumberish, - overrides?: CallOverrides, - ): Promise; - - 'depositETH(address,address,uint16)'( - lendingPool: string, - onBehalfOf: string, - referralCode: BigNumberish, - overrides?: CallOverrides, - ): Promise; - - repayETH( - lendingPool: string, - amount: BigNumberish, - rateMode: BigNumberish, - onBehalfOf: string, - overrides?: CallOverrides, - ): Promise; - - 'repayETH(address,uint256,uint256,address)'( - lendingPool: string, - amount: BigNumberish, - rateMode: BigNumberish, - onBehalfOf: string, - overrides?: CallOverrides, - ): Promise; - - withdrawETH( - lendingPool: string, - amount: BigNumberish, - onBehalfOf: string, - overrides?: CallOverrides, - ): Promise; - - 'withdrawETH(address,uint256,address)'( - lendingPool: string, - amount: BigNumberish, - onBehalfOf: string, - overrides?: CallOverrides, - ): Promise; - }; - - filters: {}; - - estimateGas: { - borrowETH( - lendingPool: string, - amount: BigNumberish, - interesRateMode: BigNumberish, - referralCode: BigNumberish, - overrides?: Overrides, - ): Promise; - - 'borrowETH(address,uint256,uint256,uint16)'( - lendingPool: string, - amount: BigNumberish, - interesRateMode: BigNumberish, - referralCode: BigNumberish, - overrides?: Overrides, - ): Promise; - - depositETH( - lendingPool: string, - onBehalfOf: string, - referralCode: BigNumberish, - overrides?: PayableOverrides, - ): Promise; - - 'depositETH(address,address,uint16)'( - lendingPool: string, - onBehalfOf: string, - referralCode: BigNumberish, - overrides?: PayableOverrides, - ): Promise; - - repayETH( - lendingPool: string, - amount: BigNumberish, - rateMode: BigNumberish, - onBehalfOf: string, - overrides?: PayableOverrides, - ): Promise; - - 'repayETH(address,uint256,uint256,address)'( - lendingPool: string, - amount: BigNumberish, - rateMode: BigNumberish, - onBehalfOf: string, - overrides?: PayableOverrides, - ): Promise; - - withdrawETH( - lendingPool: string, - amount: BigNumberish, - onBehalfOf: string, - overrides?: Overrides, - ): Promise; - - 'withdrawETH(address,uint256,address)'( - lendingPool: string, - amount: BigNumberish, - onBehalfOf: string, - overrides?: Overrides, - ): Promise; - }; - - populateTransaction: { - borrowETH( - lendingPool: string, - amount: BigNumberish, - interesRateMode: BigNumberish, - referralCode: BigNumberish, - overrides?: Overrides, - ): Promise; - - 'borrowETH(address,uint256,uint256,uint16)'( - lendingPool: string, - amount: BigNumberish, - interesRateMode: BigNumberish, - referralCode: BigNumberish, - overrides?: Overrides, - ): Promise; - - depositETH( - lendingPool: string, - onBehalfOf: string, - referralCode: BigNumberish, - overrides?: PayableOverrides, - ): Promise; - - 'depositETH(address,address,uint16)'( - lendingPool: string, - onBehalfOf: string, - referralCode: BigNumberish, - overrides?: PayableOverrides, - ): Promise; - - repayETH( - lendingPool: string, - amount: BigNumberish, - rateMode: BigNumberish, - onBehalfOf: string, - overrides?: PayableOverrides, - ): Promise; - - 'repayETH(address,uint256,uint256,address)'( - lendingPool: string, - amount: BigNumberish, - rateMode: BigNumberish, - onBehalfOf: string, - overrides?: PayableOverrides, - ): Promise; - - withdrawETH( - lendingPool: string, - amount: BigNumberish, - onBehalfOf: string, - overrides?: Overrides, - ): Promise; - - 'withdrawETH(address,uint256,address)'( - lendingPool: string, - amount: BigNumberish, - onBehalfOf: string, - overrides?: Overrides, - ): Promise; - }; -} diff --git a/packages/contract-helpers/src/wethgateway-contract/typechain/IWETHGateway__factory.ts b/packages/contract-helpers/src/wethgateway-contract/typechain/IWETHGateway__factory.ts deleted file mode 100644 index 54d978518..000000000 --- a/packages/contract-helpers/src/wethgateway-contract/typechain/IWETHGateway__factory.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ -/* eslint-disable */ - -import { Contract, Signer, utils } from 'ethers'; -import { Provider } from '@ethersproject/providers'; - -import type { IWETHGateway, IWETHGatewayInterface } from './IWETHGateway'; - -export class IWETHGateway__factory { - static connect( - address: string, - signerOrProvider: Signer | Provider, - ): IWETHGateway { - return new Contract(address, _abi, signerOrProvider) as IWETHGateway; - } - static createInterface(): IWETHGatewayInterface { - return new utils.Interface(_abi) as IWETHGatewayInterface; - } -} - -const _abi = [ - { - inputs: [ - { - internalType: 'address', - name: 'lendingPool', - type: 'address', - }, - { - internalType: 'uint256', - name: 'amount', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'interesRateMode', - type: 'uint256', - }, - { - internalType: 'uint16', - name: 'referralCode', - type: 'uint16', - }, - ], - name: 'borrowETH', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'lendingPool', - type: 'address', - }, - { - internalType: 'address', - name: 'onBehalfOf', - type: 'address', - }, - { - internalType: 'uint16', - name: 'referralCode', - type: 'uint16', - }, - ], - name: 'depositETH', - outputs: [], - stateMutability: 'payable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'lendingPool', - type: 'address', - }, - { - internalType: 'uint256', - name: 'amount', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'rateMode', - type: 'uint256', - }, - { - internalType: 'address', - name: 'onBehalfOf', - type: 'address', - }, - ], - name: 'repayETH', - outputs: [], - stateMutability: 'payable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: 'lendingPool', - type: 'address', - }, - { - internalType: 'uint256', - name: 'amount', - type: 'uint256', - }, - { - internalType: 'address', - name: 'onBehalfOf', - type: 'address', - }, - ], - name: 'withdrawETH', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, -]; diff --git a/packages/contract-helpers/src/wethgateway-contract/typechain/WrappedTokenGatewayV3.ts b/packages/contract-helpers/src/wethgateway-contract/typechain/WrappedTokenGatewayV3.ts new file mode 100644 index 000000000..0bc6d83a1 --- /dev/null +++ b/packages/contract-helpers/src/wethgateway-contract/typechain/WrappedTokenGatewayV3.ts @@ -0,0 +1,528 @@ +/* Autogenerated file. Do not edit manually. */ +/* eslint-disable */ +import type { + BaseContract, + BigNumber, + BigNumberish, + BytesLike, + CallOverrides, + ContractTransaction, + Overrides, + PayableOverrides, + PopulatedTransaction, + Signer, + utils, +} from 'ethers'; +import type { + FunctionFragment, + Result, + EventFragment, +} from '@ethersproject/abi'; +import type { Listener, Provider } from '@ethersproject/providers'; +import type { + TypedEventFilter, + TypedEvent, + TypedListener, + OnEvent, +} from './common'; + +export interface WrappedTokenGatewayV3Interface extends utils.Interface { + functions: { + 'borrowETH(address,uint256,uint16)': FunctionFragment; + 'depositETH(address,address,uint16)': FunctionFragment; + 'emergencyEtherTransfer(address,uint256)': FunctionFragment; + 'emergencyTokenTransfer(address,address,uint256)': FunctionFragment; + 'getWETHAddress()': FunctionFragment; + 'owner()': FunctionFragment; + 'renounceOwnership()': FunctionFragment; + 'repayETH(address,uint256,address)': FunctionFragment; + 'transferOwnership(address)': FunctionFragment; + 'withdrawETH(address,uint256,address)': FunctionFragment; + 'withdrawETHWithPermit(address,uint256,address,uint256,uint8,bytes32,bytes32)': FunctionFragment; + }; + + getFunction( + nameOrSignatureOrTopic: + | 'borrowETH' + | 'depositETH' + | 'emergencyEtherTransfer' + | 'emergencyTokenTransfer' + | 'getWETHAddress' + | 'owner' + | 'renounceOwnership' + | 'repayETH' + | 'transferOwnership' + | 'withdrawETH' + | 'withdrawETHWithPermit', + ): FunctionFragment; + + encodeFunctionData( + functionFragment: 'borrowETH', + values: [string, BigNumberish, BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'depositETH', + values: [string, string, BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'emergencyEtherTransfer', + values: [string, BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'emergencyTokenTransfer', + values: [string, string, BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'getWETHAddress', + values?: undefined, + ): string; + encodeFunctionData(functionFragment: 'owner', values?: undefined): string; + encodeFunctionData( + functionFragment: 'renounceOwnership', + values?: undefined, + ): string; + encodeFunctionData( + functionFragment: 'repayETH', + values: [string, BigNumberish, string], + ): string; + encodeFunctionData( + functionFragment: 'transferOwnership', + values: [string], + ): string; + encodeFunctionData( + functionFragment: 'withdrawETH', + values: [string, BigNumberish, string], + ): string; + encodeFunctionData( + functionFragment: 'withdrawETHWithPermit', + values: [ + string, + BigNumberish, + string, + BigNumberish, + BigNumberish, + BytesLike, + BytesLike, + ], + ): string; + + decodeFunctionResult(functionFragment: 'borrowETH', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'depositETH', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'emergencyEtherTransfer', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'emergencyTokenTransfer', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'getWETHAddress', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'owner', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'renounceOwnership', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'repayETH', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'transferOwnership', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'withdrawETH', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'withdrawETHWithPermit', + data: BytesLike, + ): Result; + + events: { + 'OwnershipTransferred(address,address)': EventFragment; + }; + + getEvent(nameOrSignatureOrTopic: 'OwnershipTransferred'): EventFragment; +} + +export interface OwnershipTransferredEventObject { + previousOwner: string; + newOwner: string; +} +export type OwnershipTransferredEvent = TypedEvent< + [string, string], + OwnershipTransferredEventObject +>; + +export type OwnershipTransferredEventFilter = + TypedEventFilter; + +export interface WrappedTokenGatewayV3 extends BaseContract { + connect(signerOrProvider: Signer | Provider | string): this; + attach(addressOrName: string): this; + deployed(): Promise; + + interface: WrappedTokenGatewayV3Interface; + + 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: { + borrowETH( + arg0: string, + amount: BigNumberish, + referralCode: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + depositETH( + arg0: string, + onBehalfOf: string, + referralCode: BigNumberish, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + emergencyEtherTransfer( + to: string, + amount: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + emergencyTokenTransfer( + token: string, + to: string, + amount: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + getWETHAddress(overrides?: CallOverrides): Promise<[string]>; + + owner(overrides?: CallOverrides): Promise<[string]>; + + renounceOwnership( + overrides?: Overrides & { from?: string }, + ): Promise; + + repayETH( + arg0: string, + amount: BigNumberish, + onBehalfOf: string, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + transferOwnership( + newOwner: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + withdrawETH( + arg0: string, + amount: BigNumberish, + to: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + withdrawETHWithPermit( + arg0: string, + amount: BigNumberish, + to: string, + deadline: BigNumberish, + permitV: BigNumberish, + permitR: BytesLike, + permitS: BytesLike, + overrides?: Overrides & { from?: string }, + ): Promise; + }; + + borrowETH( + arg0: string, + amount: BigNumberish, + referralCode: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + depositETH( + arg0: string, + onBehalfOf: string, + referralCode: BigNumberish, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + emergencyEtherTransfer( + to: string, + amount: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + emergencyTokenTransfer( + token: string, + to: string, + amount: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + getWETHAddress(overrides?: CallOverrides): Promise; + + owner(overrides?: CallOverrides): Promise; + + renounceOwnership( + overrides?: Overrides & { from?: string }, + ): Promise; + + repayETH( + arg0: string, + amount: BigNumberish, + onBehalfOf: string, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + transferOwnership( + newOwner: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + withdrawETH( + arg0: string, + amount: BigNumberish, + to: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + withdrawETHWithPermit( + arg0: string, + amount: BigNumberish, + to: string, + deadline: BigNumberish, + permitV: BigNumberish, + permitR: BytesLike, + permitS: BytesLike, + overrides?: Overrides & { from?: string }, + ): Promise; + + callStatic: { + borrowETH( + arg0: string, + amount: BigNumberish, + referralCode: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + depositETH( + arg0: string, + onBehalfOf: string, + referralCode: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + emergencyEtherTransfer( + to: string, + amount: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + emergencyTokenTransfer( + token: string, + to: string, + amount: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + getWETHAddress(overrides?: CallOverrides): Promise; + + owner(overrides?: CallOverrides): Promise; + + renounceOwnership(overrides?: CallOverrides): Promise; + + repayETH( + arg0: string, + amount: BigNumberish, + onBehalfOf: string, + overrides?: CallOverrides, + ): Promise; + + transferOwnership( + newOwner: string, + overrides?: CallOverrides, + ): Promise; + + withdrawETH( + arg0: string, + amount: BigNumberish, + to: string, + overrides?: CallOverrides, + ): Promise; + + withdrawETHWithPermit( + arg0: string, + amount: BigNumberish, + to: string, + deadline: BigNumberish, + permitV: BigNumberish, + permitR: BytesLike, + permitS: BytesLike, + overrides?: CallOverrides, + ): Promise; + }; + + filters: { + 'OwnershipTransferred(address,address)'( + previousOwner?: string | null, + newOwner?: string | null, + ): OwnershipTransferredEventFilter; + OwnershipTransferred( + previousOwner?: string | null, + newOwner?: string | null, + ): OwnershipTransferredEventFilter; + }; + + estimateGas: { + borrowETH( + arg0: string, + amount: BigNumberish, + referralCode: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + depositETH( + arg0: string, + onBehalfOf: string, + referralCode: BigNumberish, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + emergencyEtherTransfer( + to: string, + amount: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + emergencyTokenTransfer( + token: string, + to: string, + amount: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + getWETHAddress(overrides?: CallOverrides): Promise; + + owner(overrides?: CallOverrides): Promise; + + renounceOwnership( + overrides?: Overrides & { from?: string }, + ): Promise; + + repayETH( + arg0: string, + amount: BigNumberish, + onBehalfOf: string, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + transferOwnership( + newOwner: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + withdrawETH( + arg0: string, + amount: BigNumberish, + to: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + withdrawETHWithPermit( + arg0: string, + amount: BigNumberish, + to: string, + deadline: BigNumberish, + permitV: BigNumberish, + permitR: BytesLike, + permitS: BytesLike, + overrides?: Overrides & { from?: string }, + ): Promise; + }; + + populateTransaction: { + borrowETH( + arg0: string, + amount: BigNumberish, + referralCode: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + depositETH( + arg0: string, + onBehalfOf: string, + referralCode: BigNumberish, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + emergencyEtherTransfer( + to: string, + amount: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + emergencyTokenTransfer( + token: string, + to: string, + amount: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + getWETHAddress(overrides?: CallOverrides): Promise; + + owner(overrides?: CallOverrides): Promise; + + renounceOwnership( + overrides?: Overrides & { from?: string }, + ): Promise; + + repayETH( + arg0: string, + amount: BigNumberish, + onBehalfOf: string, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + transferOwnership( + newOwner: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + withdrawETH( + arg0: string, + amount: BigNumberish, + to: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + withdrawETHWithPermit( + arg0: string, + amount: BigNumberish, + to: string, + deadline: BigNumberish, + permitV: BigNumberish, + permitR: BytesLike, + permitS: BytesLike, + overrides?: Overrides & { from?: string }, + ): Promise; + }; +} diff --git a/packages/contract-helpers/src/wethgateway-contract/typechain/WrappedTokenGatewayV3__factory.ts b/packages/contract-helpers/src/wethgateway-contract/typechain/WrappedTokenGatewayV3__factory.ts new file mode 100644 index 000000000..7dec97548 --- /dev/null +++ b/packages/contract-helpers/src/wethgateway-contract/typechain/WrappedTokenGatewayV3__factory.ts @@ -0,0 +1,299 @@ +/* Autogenerated file. Do not edit manually. */ +/* eslint-disable */ + +import { Contract, Signer, utils } from 'ethers'; +import type { Provider } from '@ethersproject/providers'; +import type { + WrappedTokenGatewayV3, + WrappedTokenGatewayV3Interface, +} from './WrappedTokenGatewayV3'; + +const _abi = [ + { + inputs: [ + { + internalType: 'address', + name: 'weth', + type: 'address', + }, + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + internalType: 'contract IPool', + name: 'pool', + type: 'address', + }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'previousOwner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'OwnershipTransferred', + type: 'event', + }, + { + stateMutability: 'payable', + type: 'fallback', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint16', + name: 'referralCode', + type: 'uint16', + }, + ], + name: 'borrowETH', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + { + internalType: 'address', + name: 'onBehalfOf', + type: 'address', + }, + { + internalType: 'uint16', + name: 'referralCode', + type: 'uint16', + }, + ], + name: 'depositETH', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'emergencyEtherTransfer', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'token', + type: 'address', + }, + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'emergencyTokenTransfer', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'getWETHAddress', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'owner', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'renounceOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'address', + name: 'onBehalfOf', + type: 'address', + }, + ], + name: 'repayETH', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'transferOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'address', + name: 'to', + type: 'address', + }, + ], + name: 'withdrawETH', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'deadline', + type: 'uint256', + }, + { + internalType: 'uint8', + name: 'permitV', + type: 'uint8', + }, + { + internalType: 'bytes32', + name: 'permitR', + type: 'bytes32', + }, + { + internalType: 'bytes32', + name: 'permitS', + type: 'bytes32', + }, + ], + name: 'withdrawETHWithPermit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + stateMutability: 'payable', + type: 'receive', + }, +] as const; + +export class WrappedTokenGatewayV3__factory { + static readonly abi = _abi; + static createInterface(): WrappedTokenGatewayV3Interface { + return new utils.Interface(_abi) as WrappedTokenGatewayV3Interface; + } + static connect( + address: string, + signerOrProvider: Signer | Provider, + ): WrappedTokenGatewayV3 { + return new Contract( + address, + _abi, + signerOrProvider, + ) as WrappedTokenGatewayV3; + } +} diff --git a/packages/contract-helpers/src/wethgateway-contract/typechain/common.ts b/packages/contract-helpers/src/wethgateway-contract/typechain/common.ts new file mode 100644 index 000000000..0fb82cd37 --- /dev/null +++ b/packages/contract-helpers/src/wethgateway-contract/typechain/common.ts @@ -0,0 +1,43 @@ +/* Autogenerated file. Do not edit manually. */ +/* eslint-disable */ +import type { Listener } from '@ethersproject/providers'; +import type { Event, EventFilter } from 'ethers'; + +export interface TypedEvent< + TArgsArray extends Array = any, + TArgsObject = any, +> extends Event { + args: TArgsArray & TArgsObject; +} + +export interface TypedEventFilter<_TEvent extends TypedEvent> + extends EventFilter {} + +export interface TypedListener { + (...listenerArg: [...__TypechainArgsArray, TEvent]): void; +} + +type __TypechainArgsArray = T extends TypedEvent ? U : never; + +export interface OnEvent { + ( + eventFilter: TypedEventFilter, + listener: TypedListener, + ): TRes; + (eventName: string, listener: Listener): TRes; +} + +export type MinEthersFactory = { + deploy(...a: ARGS[]): Promise; +}; + +export type GetContractTypeFromFactory = F extends MinEthersFactory< + infer C, + any +> + ? C + : never; + +export type GetARGsTypeFromFactory = F extends MinEthersFactory + ? Parameters + : never; diff --git a/packages/contract-helpers/src/wethgateway-contract/wethGateway.test.ts b/packages/contract-helpers/src/wethgateway-contract/wethGateway.test.ts index 5b6ef6db2..29886cb93 100644 --- a/packages/contract-helpers/src/wethgateway-contract/wethGateway.test.ts +++ b/packages/contract-helpers/src/wethgateway-contract/wethGateway.test.ts @@ -1,10 +1,5 @@ import { BigNumber, constants, providers, utils } from 'ethers'; -import { - eEthereumTxType, - GasType, - InterestRate, - transactionType, -} from '../commons/types'; +import { eEthereumTxType, GasType, transactionType } from '../commons/types'; import { valueToWei } from '../commons/utils'; import { ERC20Service } from '../erc20-contract'; import { WETHGatewayService } from './index'; @@ -90,26 +85,24 @@ describe('WethGatewayService', () => { lendingPool, user, amount: '1', - interestRateMode: InterestRate.Variable, referralCode: '0', }); expect(txData.to).toEqual(wethGatewayAddress); expect(txData.from).toEqual(user); expect(txData.data).toEqual( - '0x66514c970000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000', + '0xe74f7b85000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000', ); const txDataStable = weth.generateBorrowEthTxData({ lendingPool, user, amount: '1', - interestRateMode: InterestRate.Stable, debtTokenAddress: '', }); expect(txDataStable.data).toEqual( - '0x66514c970000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000', + '0xe74f7b85000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000', ); }); }); @@ -127,13 +120,12 @@ describe('WethGatewayService', () => { lendingPool, user, amount: '1', - interestRateMode: InterestRate.Variable, }); expect(txData.to).toEqual(wethGatewayAddress); expect(txData.from).toEqual(user); expect(txData.data).toEqual( - '0x02c5fcf80000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003', + '0xbcc3c255000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003', ); const onBehalfOf = '0x0000000000000000000000000000000000000004'; @@ -143,36 +135,12 @@ describe('WethGatewayService', () => { user, amount: '1', onBehalfOf, - interestRateMode: InterestRate.Variable, }); expect(txDataUpdatedParams.to).toEqual(wethGatewayAddress); expect(txDataUpdatedParams.from).toEqual(user); expect(txDataUpdatedParams.data).toEqual( - '0x02c5fcf80000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004', - ); - }); - - it('generates repayETH tx data with stable debt', () => { - const provider: providers.Provider = new providers.JsonRpcProvider(); - const erc20Service = new ERC20Service(provider); - const weth = new WETHGatewayService( - provider, - erc20Service, - wethGatewayAddress, - ); - const user = '0x0000000000000000000000000000000000000003'; - const txData = weth.generateRepayEthTxData({ - lendingPool, - user, - amount: '1', - interestRateMode: InterestRate.Stable, - }); - - expect(txData.to).toEqual(wethGatewayAddress); - expect(txData.from).toEqual(user); - expect(txData.data).toEqual( - '0x02c5fcf80000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003', + '0xbcc3c255000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000004', ); }); }); @@ -414,7 +382,6 @@ describe('WethGatewayService', () => { describe('withdrawETH', () => { const user = '0x0000000000000000000000000000000000000003'; const debtTokenAddress = '0x0000000000000000000000000000000000000005'; - const interestRateMode = InterestRate.Stable; const amount = '123.456'; const referralCode = '0'; const provider: providers.Provider = new providers.JsonRpcProvider(); @@ -426,7 +393,7 @@ describe('WethGatewayService', () => { afterEach(() => { jest.clearAllMocks(); }); - it('Expects the borrow tx object to be correct with all params and variable stable rate without approval', async () => { + it('Expects the borrow tx object to be correct with all params and variable rate without approval', async () => { const weth = new WETHGatewayService( provider, erc20Service, @@ -447,13 +414,11 @@ describe('WethGatewayService', () => { gasPrice: '1', }), })); - const interestRateMode = InterestRate.Variable; const txObj = await weth.borrowETH({ lendingPool, user, amount, debtTokenAddress, - interestRateMode, referralCode, }); @@ -462,11 +427,10 @@ describe('WethGatewayService', () => { lendingPool, user, amount, - interestRateMode, referralCode, }), ).rejects.toThrowError( - `To borrow ETH you need to pass the stable or variable WETH debt Token Address corresponding the interestRateMode`, + `To borrow ETH you need to pass the variable WETH debt Token Address`, ); expect(isApprovedSpy).toHaveBeenCalled(); @@ -480,14 +444,13 @@ describe('WethGatewayService', () => { expect(tx.gasLimit).toEqual(BigNumber.from(1)); const decoded = utils.defaultAbiCoder.decode( - ['address', 'uint256', 'uint256', 'uint16'], + ['address', 'uint256', 'uint16'], utils.hexDataSlice(tx.data ?? '', 4), ); expect(decoded[0]).toEqual(lendingPool); expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); - expect(decoded[2]).toEqual(BigNumber.from(2)); - expect(decoded[3]).toEqual(Number(referralCode)); + expect(decoded[2]).toEqual(Number(referralCode)); // gas price const gasPrice: GasType | null = await txObj[1].gas(); @@ -495,7 +458,7 @@ describe('WethGatewayService', () => { expect(gasPrice?.gasLimit).toEqual('450000'); expect(gasPrice?.gasPrice).toEqual('1'); }); - it('Expects the borrow tx object to be correct with all params and stable stable rate already approved', async () => { + it('Expects the borrow tx object to be correct with all params and variable rate already approved', async () => { const weth = new WETHGatewayService( provider, erc20Service, @@ -511,7 +474,6 @@ describe('WethGatewayService', () => { user, amount, debtTokenAddress, - interestRateMode, referralCode, }); @@ -525,60 +487,13 @@ describe('WethGatewayService', () => { expect(tx.gasLimit).toEqual(BigNumber.from(1)); const decoded = utils.defaultAbiCoder.decode( - ['address', 'uint256', 'uint256', 'uint16'], + ['address', 'uint256', 'uint16'], utils.hexDataSlice(tx.data ?? '', 4), ); expect(decoded[0]).toEqual(lendingPool); expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); - expect(decoded[2]).toEqual(BigNumber.from(1)); - expect(decoded[3]).toEqual(Number(referralCode)); - - // gas price - const gasPrice: GasType | null = await txObj[0].gas(); - expect(gasPrice).not.toBeNull(); - expect(gasPrice?.gasLimit).toEqual('1'); - expect(gasPrice?.gasPrice).toEqual('1'); - }); - it('Expects the borrow tx object to be correct with all params and none stable rate', async () => { - const weth = new WETHGatewayService( - provider, - erc20Service, - wethGatewayAddress, - ); - - const isApprovedSpy = jest - .spyOn(weth.baseDebtTokenService, 'isDelegationApproved') - .mockImplementation(async () => Promise.resolve(true)); - - const interestRateMode = InterestRate.None; - const txObj = await weth.borrowETH({ - lendingPool, - user, - amount, - debtTokenAddress, - interestRateMode, - referralCode, - }); - - expect(isApprovedSpy).toHaveBeenCalled(); - expect(txObj.length).toEqual(1); - expect(txObj[0].txType).toEqual(eEthereumTxType.DLP_ACTION); - - const tx: transactionType = await txObj[0].tx(); - expect(tx.to).toEqual(wethGatewayAddress); - expect(tx.from).toEqual(user); - expect(tx.gasLimit).toEqual(BigNumber.from(1)); - - const decoded = utils.defaultAbiCoder.decode( - ['address', 'uint256', 'uint256', 'uint16'], - utils.hexDataSlice(tx.data ?? '', 4), - ); - - expect(decoded[0]).toEqual(lendingPool); - expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); - expect(decoded[2]).toEqual(BigNumber.from(1)); - expect(decoded[3]).toEqual(Number(referralCode)); + expect(decoded[2]).toEqual(Number(referralCode)); // gas price const gasPrice: GasType | null = await txObj[0].gas(); @@ -597,13 +512,11 @@ describe('WethGatewayService', () => { .spyOn(weth.baseDebtTokenService, 'isDelegationApproved') .mockImplementation(async () => Promise.resolve(true)); - const interestRateMode = InterestRate.None; const txObj = await weth.borrowETH({ lendingPool, user, amount, debtTokenAddress, - interestRateMode, }); expect(isApprovedSpy).toHaveBeenCalled(); @@ -616,14 +529,13 @@ describe('WethGatewayService', () => { expect(tx.gasLimit).toEqual(BigNumber.from(1)); const decoded = utils.defaultAbiCoder.decode( - ['address', 'uint256', 'uint256', 'uint16'], + ['address', 'uint256', 'uint16'], utils.hexDataSlice(tx.data ?? '', 4), ); expect(decoded[0]).toEqual(lendingPool); expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); - expect(decoded[2]).toEqual(BigNumber.from(1)); - expect(decoded[3]).toEqual(0); + expect(decoded[2]).toEqual(0); // gas price const gasPrice: GasType | null = await txObj[0].gas(); @@ -639,7 +551,6 @@ describe('WethGatewayService', () => { user, amount, debtTokenAddress, - interestRateMode, referralCode, }); @@ -658,7 +569,6 @@ describe('WethGatewayService', () => { user, amount, debtTokenAddress, - interestRateMode, referralCode, }), ).rejects.toThrowError( @@ -678,7 +588,6 @@ describe('WethGatewayService', () => { user, amount, debtTokenAddress, - interestRateMode, referralCode, }), ).rejects.toThrowError( @@ -698,7 +607,6 @@ describe('WethGatewayService', () => { user, amount, debtTokenAddress, - interestRateMode, referralCode, }), ).rejects.toThrowError(`Amount: ${amount} needs to be greater than 0`); @@ -716,7 +624,6 @@ describe('WethGatewayService', () => { user, amount, debtTokenAddress, - interestRateMode, referralCode, }), ).rejects.toThrowError(`Amount: ${amount} needs to be greater than 0`); @@ -734,7 +641,6 @@ describe('WethGatewayService', () => { user, amount, debtTokenAddress, - interestRateMode, referralCode, }), ).rejects.toThrowError( @@ -754,7 +660,6 @@ describe('WethGatewayService', () => { user, amount, debtTokenAddress, - interestRateMode, referralCode, }), ).rejects.toThrowError( @@ -1035,7 +940,6 @@ describe('WethGatewayService', () => { describe('borrowETH', () => { const user = '0x0000000000000000000000000000000000000003'; const onBehalfOf = '0x0000000000000000000000000000000000000004'; - const interestRateMode = InterestRate.Stable; const amount = '123.456'; const provider: providers.Provider = new providers.JsonRpcProvider(); jest @@ -1046,57 +950,16 @@ describe('WethGatewayService', () => { afterEach(() => { jest.clearAllMocks(); }); - it('Expects the repay tx object to be correct with all params and stable rate mode', async () => { - const weth = new WETHGatewayService( - provider, - erc20Service, - wethGatewayAddress, - ); - - const txObj = weth.repayETH({ - lendingPool, - user, - amount, - interestRateMode, - onBehalfOf, - }); - - expect(txObj.length).toEqual(1); - expect(txObj[0].txType).toEqual(eEthereumTxType.DLP_ACTION); - - const tx: transactionType = await txObj[0].tx(); - expect(tx.to).toEqual(wethGatewayAddress); - expect(tx.from).toEqual(user); - expect(tx.gasLimit).toEqual(BigNumber.from(1)); - - const decoded = utils.defaultAbiCoder.decode( - ['address', 'uint256', 'uint256', 'address'], - utils.hexDataSlice(tx.data ?? '', 4), - ); - - expect(decoded[0]).toEqual(lendingPool); - expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); - expect(decoded[2]).toEqual(BigNumber.from(1)); - expect(decoded[3]).toEqual(onBehalfOf); - - // gas price - const gasPrice: GasType | null = await txObj[0].gas(); - expect(gasPrice).not.toBeNull(); - expect(gasPrice?.gasLimit).toEqual('1'); - expect(gasPrice?.gasPrice).toEqual('1'); - }); - it('Expects the repay tx object to be correct with all params and variable rate mode', async () => { + it('Expects the repay tx object to be correct with all params', async () => { const weth = new WETHGatewayService( provider, erc20Service, wethGatewayAddress, ); - const interestRateMode = InterestRate.Variable; const txObj = weth.repayETH({ lendingPool, user, amount, - interestRateMode, onBehalfOf, }); @@ -1109,53 +972,13 @@ describe('WethGatewayService', () => { expect(tx.gasLimit).toEqual(BigNumber.from(1)); const decoded = utils.defaultAbiCoder.decode( - ['address', 'uint256', 'uint256', 'address'], - utils.hexDataSlice(tx.data ?? '', 4), - ); - - expect(decoded[0]).toEqual(lendingPool); - expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); - expect(decoded[2]).toEqual(BigNumber.from(2)); - expect(decoded[3]).toEqual(onBehalfOf); - - // gas price - const gasPrice: GasType | null = await txObj[0].gas(); - expect(gasPrice).not.toBeNull(); - expect(gasPrice?.gasLimit).toEqual('1'); - expect(gasPrice?.gasPrice).toEqual('1'); - }); - it('Expects the repay tx object to be correct with all params and none rate mode', async () => { - const weth = new WETHGatewayService( - provider, - erc20Service, - wethGatewayAddress, - ); - const interestRateMode = InterestRate.None; - const txObj = weth.repayETH({ - lendingPool, - user, - amount, - interestRateMode, - onBehalfOf, - }); - - expect(txObj.length).toEqual(1); - expect(txObj[0].txType).toEqual(eEthereumTxType.DLP_ACTION); - - const tx: transactionType = await txObj[0].tx(); - expect(tx.to).toEqual(wethGatewayAddress); - expect(tx.from).toEqual(user); - expect(tx.gasLimit).toEqual(BigNumber.from(1)); - - const decoded = utils.defaultAbiCoder.decode( - ['address', 'uint256', 'uint256', 'address'], + ['address', 'uint256', 'address'], utils.hexDataSlice(tx.data ?? '', 4), ); expect(decoded[0]).toEqual(lendingPool); expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); - expect(decoded[2]).toEqual(BigNumber.from(1)); - expect(decoded[3]).toEqual(onBehalfOf); + expect(decoded[2]).toEqual(onBehalfOf); // gas price const gasPrice: GasType | null = await txObj[0].gas(); @@ -1174,7 +997,6 @@ describe('WethGatewayService', () => { lendingPool, user, amount, - interestRateMode, }); expect(txObj.length).toEqual(1); @@ -1186,14 +1008,13 @@ describe('WethGatewayService', () => { expect(tx.gasLimit).toEqual(BigNumber.from(1)); const decoded = utils.defaultAbiCoder.decode( - ['address', 'uint256', 'uint256', 'address'], + ['address', 'uint256', 'address'], utils.hexDataSlice(tx.data ?? '', 4), ); expect(decoded[0]).toEqual(lendingPool); expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); - expect(decoded[2]).toEqual(BigNumber.from(1)); - expect(decoded[3]).toEqual(user); + expect(decoded[2]).toEqual(user); // gas price const gasPrice: GasType | null = await txObj[0].gas(); @@ -1208,7 +1029,6 @@ describe('WethGatewayService', () => { lendingPool, user, amount, - interestRateMode, }); expect(txObj.length).toEqual(0); @@ -1225,7 +1045,6 @@ describe('WethGatewayService', () => { lendingPool, user, amount, - interestRateMode, }), ).toThrowError(`Address: ${user} is not a valid ethereum Address`); }); @@ -1241,7 +1060,6 @@ describe('WethGatewayService', () => { lendingPool, user, amount, - interestRateMode, }), ).toThrowError(`Address: ${lendingPool} is not a valid ethereum Address`); }); @@ -1257,7 +1075,6 @@ describe('WethGatewayService', () => { lendingPool, user, amount, - interestRateMode, }), ).toThrowError(`Amount: ${amount} needs to be greater than 0`); }); @@ -1273,7 +1090,6 @@ describe('WethGatewayService', () => { lendingPool, user, amount, - interestRateMode, }), ).toThrowError(`Amount: ${amount} needs to be greater than 0`); }); @@ -1289,7 +1105,6 @@ describe('WethGatewayService', () => { lendingPool, user, amount, - interestRateMode, onBehalfOf, }), ).toThrowError(`Address: ${onBehalfOf} is not a valid ethereum Address`); From e2cebce4b6b27c98badecef37fcbbb35cd91987c Mon Sep 17 00:00:00 2001 From: Joaquin Battilana Date: Tue, 8 Oct 2024 18:46:41 +0100 Subject: [PATCH 2/4] feat: fixed test for wrapped new interface --- .../src/lendingPool-contract-bundle/index.ts | 4 +--- .../lendingPoolBundle.test.ts | 6 ++--- .../src/lendingPool-contract/index.ts | 2 -- .../src/v3-pool-contract-bundle/index.ts | 2 -- .../pool-bundle.test.ts | 22 +++---------------- .../src/v3-pool-contract/index.ts | 2 -- 6 files changed, 7 insertions(+), 31 deletions(-) diff --git a/packages/contract-helpers/src/lendingPool-contract-bundle/index.ts b/packages/contract-helpers/src/lendingPool-contract-bundle/index.ts index e3a19f887..83a9a0605 100644 --- a/packages/contract-helpers/src/lendingPool-contract-bundle/index.ts +++ b/packages/contract-helpers/src/lendingPool-contract-bundle/index.ts @@ -164,7 +164,7 @@ export class LendingPoolBundle if (reserve.toLowerCase() === API_ETH_MOCK_ADDRESS.toLowerCase()) { if (!debtTokenAddress) { throw new Error( - `To borrow ETH you need to pass the stable or variable WETH debt Token Address corresponding the interestRateMode`, + `To borrow ETH you need to pass the variable WETH debt Token Address`, ); } @@ -173,7 +173,6 @@ export class LendingPoolBundle user, amount, debtTokenAddress, - interestRateMode, referralCode: referralCodeParam, }); } else { @@ -209,7 +208,6 @@ export class LendingPoolBundle lendingPool: this.lendingPoolAddress, user, amount, - interestRateMode, onBehalfOf, }); } diff --git a/packages/contract-helpers/src/lendingPool-contract-bundle/lendingPoolBundle.test.ts b/packages/contract-helpers/src/lendingPool-contract-bundle/lendingPoolBundle.test.ts index 104a41efc..056ca73b2 100644 --- a/packages/contract-helpers/src/lendingPool-contract-bundle/lendingPoolBundle.test.ts +++ b/packages/contract-helpers/src/lendingPool-contract-bundle/lendingPoolBundle.test.ts @@ -220,7 +220,7 @@ describe('LendingPoolBundle', () => { expect(result.from).toEqual(USER); expect(result.value).toEqual(undefined); expect(result.data).toEqual( - '0x66514c970000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000', + '0xe74f7b85000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000', ); await expect(async () => @@ -233,7 +233,7 @@ describe('LendingPoolBundle', () => { referralCode: '0', }), ).rejects.toThrowError( - `To borrow ETH you need to pass the stable or variable WETH debt Token Address corresponding the interestRateMode`, + `To borrow ETH you need to pass the variable WETH debt Token Address`, ); }); }); @@ -300,7 +300,7 @@ describe('LendingPoolBundle', () => { expect(result.from).toEqual(USER); expect(result.value).toEqual(BigNumber.from('1')); expect(result.data).toEqual( - '0x02c5fcf80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003', + '0xbcc3c255000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003', ); }); diff --git a/packages/contract-helpers/src/lendingPool-contract/index.ts b/packages/contract-helpers/src/lendingPool-contract/index.ts index b58a2c12b..062dc33c5 100644 --- a/packages/contract-helpers/src/lendingPool-contract/index.ts +++ b/packages/contract-helpers/src/lendingPool-contract/index.ts @@ -367,7 +367,6 @@ export class LendingPool user, amount, debtTokenAddress, - interestRateMode, referralCode, }); } @@ -421,7 +420,6 @@ export class LendingPool lendingPool: this.lendingPoolAddress, user, amount, - interestRateMode, onBehalfOf, }); } 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 767957aa1..cfdce5139 100644 --- a/packages/contract-helpers/src/v3-pool-contract-bundle/index.ts +++ b/packages/contract-helpers/src/v3-pool-contract-bundle/index.ts @@ -316,7 +316,6 @@ export class PoolBundle user, amount, debtTokenAddress, - interestRateMode, referralCode: referralCodeParam, }); } else { @@ -375,7 +374,6 @@ export class PoolBundle lendingPool: this.poolAddress, user, amount, - interestRateMode, onBehalfOf: onBehalfOfParam, }); } 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 2224aae3a..fed24e17f 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 @@ -475,16 +475,6 @@ describe('PoolBundle', () => { debtTokenAddress: API_ETH_MOCK_ADDRESS.toLowerCase(), }); - const resultStable = instance.borrowTxBuilder.generateTxData({ - user: USER, - reserve: API_ETH_MOCK_ADDRESS.toLowerCase(), - amount: '1', - onBehalfOf: USER, - interestRateMode: InterestRate.Stable, - referralCode: '0', - debtTokenAddress: API_ETH_MOCK_ADDRESS.toLowerCase(), - }); - const differentParamsSameResult = instance.borrowTxBuilder.generateTxData( { user: USER, @@ -495,9 +485,7 @@ describe('PoolBundle', () => { }, ); const variableTxData = - '0x66514c970000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000'; - const stableTxData = - '0x66514c970000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000'; + '0xe74f7b85000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000'; expect(result.to).toEqual(WETH_GATEWAY); expect(result.from).toEqual(USER); expect(result.value).toEqual(undefined); @@ -505,10 +493,6 @@ describe('PoolBundle', () => { expect(differentParamsSameResult.to).toEqual(WETH_GATEWAY); expect(differentParamsSameResult.from).toEqual(USER); expect(differentParamsSameResult.data).toEqual(variableTxData); - expect(resultStable.to).toEqual(WETH_GATEWAY); - expect(resultStable.from).toEqual(USER); - expect(resultStable.value).toEqual(undefined); - expect(resultStable.data).toEqual(stableTxData); }); it('generates borrow tx data with generateTxData and L2POOL', () => { @@ -711,12 +695,12 @@ describe('PoolBundle', () => { expect(result.from).toEqual(USER); expect(result.value).toEqual(BigNumber.from('1')); expect(result.data).toEqual( - '0x02c5fcf80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003', + '0xbcc3c255000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003', ); expect(differentParamsSameResult.to).toEqual(WETH_GATEWAY); expect(differentParamsSameResult.from).toEqual(USER); expect(differentParamsSameResult.data).toEqual( - '0x02c5fcf80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003', + '0xbcc3c255000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003', ); }); diff --git a/packages/contract-helpers/src/v3-pool-contract/index.ts b/packages/contract-helpers/src/v3-pool-contract/index.ts index 4e4e025b1..7135c6df3 100644 --- a/packages/contract-helpers/src/v3-pool-contract/index.ts +++ b/packages/contract-helpers/src/v3-pool-contract/index.ts @@ -681,7 +681,6 @@ export class Pool extends BaseService implements PoolInterface { user, amount, debtTokenAddress, - interestRateMode, referralCode, }); } @@ -750,7 +749,6 @@ export class Pool extends BaseService implements PoolInterface { lendingPool: this.poolAddress, user, amount, - interestRateMode, onBehalfOf, }); } From fbf8ed9db1c82b6774ac919b06ae562fecdfbeb8 Mon Sep 17 00:00:00 2001 From: Joaquin Battilana Date: Wed, 9 Oct 2024 01:11:45 +0100 Subject: [PATCH 3/4] feat: added renamed v3 gateway --- .../contract-helpers/src/lendingPool-contract-bundle/index.ts | 2 +- packages/contract-helpers/src/lendingPool-contract/index.ts | 2 +- packages/contract-helpers/src/v3-pool-contract-bundle/index.ts | 2 +- packages/contract-helpers/src/v3-pool-contract/index.ts | 2 +- .../{wethgateway-contract => v3-wethgateway-contract}/index.ts | 0 .../typechain/WrappedTokenGatewayV3.ts | 0 .../typechain/WrappedTokenGatewayV3__factory.ts | 0 .../typechain/common.ts | 0 .../wethGateway.test.ts | 0 9 files changed, 4 insertions(+), 4 deletions(-) rename packages/contract-helpers/src/{wethgateway-contract => v3-wethgateway-contract}/index.ts (100%) rename packages/contract-helpers/src/{wethgateway-contract => v3-wethgateway-contract}/typechain/WrappedTokenGatewayV3.ts (100%) rename packages/contract-helpers/src/{wethgateway-contract => v3-wethgateway-contract}/typechain/WrappedTokenGatewayV3__factory.ts (100%) rename packages/contract-helpers/src/{wethgateway-contract => v3-wethgateway-contract}/typechain/common.ts (100%) rename packages/contract-helpers/src/{wethgateway-contract => v3-wethgateway-contract}/wethGateway.test.ts (100%) diff --git a/packages/contract-helpers/src/lendingPool-contract-bundle/index.ts b/packages/contract-helpers/src/lendingPool-contract-bundle/index.ts index 83a9a0605..4de20b175 100644 --- a/packages/contract-helpers/src/lendingPool-contract-bundle/index.ts +++ b/packages/contract-helpers/src/lendingPool-contract-bundle/index.ts @@ -31,7 +31,7 @@ import { SynthetixInterface, SynthetixService } from '../synthetix-contract'; import { WETHGatewayInterface, WETHGatewayService, -} from '../wethgateway-contract'; +} from '../v3-wethgateway-contract'; export type DepositTxBuilder = { generateTxData: ({ diff --git a/packages/contract-helpers/src/lendingPool-contract/index.ts b/packages/contract-helpers/src/lendingPool-contract/index.ts index 062dc33c5..bb76f8556 100644 --- a/packages/contract-helpers/src/lendingPool-contract/index.ts +++ b/packages/contract-helpers/src/lendingPool-contract/index.ts @@ -46,7 +46,7 @@ import { SynthetixInterface, SynthetixService } from '../synthetix-contract'; import { WETHGatewayInterface, WETHGatewayService, -} from '../wethgateway-contract'; +} from '../v3-wethgateway-contract'; import { LPBorrowParamsType, LPDepositParamsType, 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 cfdce5139..ff4dae44b 100644 --- a/packages/contract-helpers/src/v3-pool-contract-bundle/index.ts +++ b/packages/contract-helpers/src/v3-pool-contract-bundle/index.ts @@ -37,7 +37,7 @@ import { L2Pool, L2PoolInterface } from '../v3-pool-rollups'; import { WETHGatewayInterface, WETHGatewayService, -} from '../wethgateway-contract'; +} from '../v3-wethgateway-contract'; export type SupplyTxBuilder = { generateTxData: ({ diff --git a/packages/contract-helpers/src/v3-pool-contract/index.ts b/packages/contract-helpers/src/v3-pool-contract/index.ts index 7135c6df3..cdc252e69 100644 --- a/packages/contract-helpers/src/v3-pool-contract/index.ts +++ b/packages/contract-helpers/src/v3-pool-contract/index.ts @@ -53,7 +53,7 @@ import { L2Pool, L2PoolInterface } from '../v3-pool-rollups'; import { WETHGatewayInterface, WETHGatewayService, -} from '../wethgateway-contract'; +} from '../v3-wethgateway-contract'; import { LPBorrowParamsType, LPSupplyParamsType, diff --git a/packages/contract-helpers/src/wethgateway-contract/index.ts b/packages/contract-helpers/src/v3-wethgateway-contract/index.ts similarity index 100% rename from packages/contract-helpers/src/wethgateway-contract/index.ts rename to packages/contract-helpers/src/v3-wethgateway-contract/index.ts diff --git a/packages/contract-helpers/src/wethgateway-contract/typechain/WrappedTokenGatewayV3.ts b/packages/contract-helpers/src/v3-wethgateway-contract/typechain/WrappedTokenGatewayV3.ts similarity index 100% rename from packages/contract-helpers/src/wethgateway-contract/typechain/WrappedTokenGatewayV3.ts rename to packages/contract-helpers/src/v3-wethgateway-contract/typechain/WrappedTokenGatewayV3.ts diff --git a/packages/contract-helpers/src/wethgateway-contract/typechain/WrappedTokenGatewayV3__factory.ts b/packages/contract-helpers/src/v3-wethgateway-contract/typechain/WrappedTokenGatewayV3__factory.ts similarity index 100% rename from packages/contract-helpers/src/wethgateway-contract/typechain/WrappedTokenGatewayV3__factory.ts rename to packages/contract-helpers/src/v3-wethgateway-contract/typechain/WrappedTokenGatewayV3__factory.ts diff --git a/packages/contract-helpers/src/wethgateway-contract/typechain/common.ts b/packages/contract-helpers/src/v3-wethgateway-contract/typechain/common.ts similarity index 100% rename from packages/contract-helpers/src/wethgateway-contract/typechain/common.ts rename to packages/contract-helpers/src/v3-wethgateway-contract/typechain/common.ts diff --git a/packages/contract-helpers/src/wethgateway-contract/wethGateway.test.ts b/packages/contract-helpers/src/v3-wethgateway-contract/wethGateway.test.ts similarity index 100% rename from packages/contract-helpers/src/wethgateway-contract/wethGateway.test.ts rename to packages/contract-helpers/src/v3-wethgateway-contract/wethGateway.test.ts From fc3cb9526a289714342f8ebd205f30e20cccdacb Mon Sep 17 00:00:00 2001 From: Joaquin Battilana Date: Wed, 9 Oct 2024 01:20:28 +0100 Subject: [PATCH 4/4] feat: go back to old pool weth gateway for v2 --- .../src/lendingPool-contract-bundle/index.ts | 6 +- .../lendingPoolBundle.test.ts | 6 +- .../src/lendingPool-contract/index.ts | 4 +- .../src/wethgateway-contract/index.ts | 408 ++++++ .../typechain/IWETHGateway.d.ts | 381 +++++ .../typechain/IWETHGateway__factory.ts | 124 ++ .../wethgateway-contract/wethGateway.test.ts | 1298 +++++++++++++++++ 7 files changed, 2221 insertions(+), 6 deletions(-) create mode 100644 packages/contract-helpers/src/wethgateway-contract/index.ts create mode 100644 packages/contract-helpers/src/wethgateway-contract/typechain/IWETHGateway.d.ts create mode 100644 packages/contract-helpers/src/wethgateway-contract/typechain/IWETHGateway__factory.ts create mode 100644 packages/contract-helpers/src/wethgateway-contract/wethGateway.test.ts diff --git a/packages/contract-helpers/src/lendingPool-contract-bundle/index.ts b/packages/contract-helpers/src/lendingPool-contract-bundle/index.ts index 4de20b175..e3a19f887 100644 --- a/packages/contract-helpers/src/lendingPool-contract-bundle/index.ts +++ b/packages/contract-helpers/src/lendingPool-contract-bundle/index.ts @@ -31,7 +31,7 @@ import { SynthetixInterface, SynthetixService } from '../synthetix-contract'; import { WETHGatewayInterface, WETHGatewayService, -} from '../v3-wethgateway-contract'; +} from '../wethgateway-contract'; export type DepositTxBuilder = { generateTxData: ({ @@ -164,7 +164,7 @@ export class LendingPoolBundle if (reserve.toLowerCase() === API_ETH_MOCK_ADDRESS.toLowerCase()) { if (!debtTokenAddress) { throw new Error( - `To borrow ETH you need to pass the variable WETH debt Token Address`, + `To borrow ETH you need to pass the stable or variable WETH debt Token Address corresponding the interestRateMode`, ); } @@ -173,6 +173,7 @@ export class LendingPoolBundle user, amount, debtTokenAddress, + interestRateMode, referralCode: referralCodeParam, }); } else { @@ -208,6 +209,7 @@ export class LendingPoolBundle lendingPool: this.lendingPoolAddress, user, amount, + interestRateMode, onBehalfOf, }); } diff --git a/packages/contract-helpers/src/lendingPool-contract-bundle/lendingPoolBundle.test.ts b/packages/contract-helpers/src/lendingPool-contract-bundle/lendingPoolBundle.test.ts index 056ca73b2..104a41efc 100644 --- a/packages/contract-helpers/src/lendingPool-contract-bundle/lendingPoolBundle.test.ts +++ b/packages/contract-helpers/src/lendingPool-contract-bundle/lendingPoolBundle.test.ts @@ -220,7 +220,7 @@ describe('LendingPoolBundle', () => { expect(result.from).toEqual(USER); expect(result.value).toEqual(undefined); expect(result.data).toEqual( - '0xe74f7b85000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000', + '0x66514c970000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000', ); await expect(async () => @@ -233,7 +233,7 @@ describe('LendingPoolBundle', () => { referralCode: '0', }), ).rejects.toThrowError( - `To borrow ETH you need to pass the variable WETH debt Token Address`, + `To borrow ETH you need to pass the stable or variable WETH debt Token Address corresponding the interestRateMode`, ); }); }); @@ -300,7 +300,7 @@ describe('LendingPoolBundle', () => { expect(result.from).toEqual(USER); expect(result.value).toEqual(BigNumber.from('1')); expect(result.data).toEqual( - '0xbcc3c255000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003', + '0x02c5fcf80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003', ); }); diff --git a/packages/contract-helpers/src/lendingPool-contract/index.ts b/packages/contract-helpers/src/lendingPool-contract/index.ts index bb76f8556..b58a2c12b 100644 --- a/packages/contract-helpers/src/lendingPool-contract/index.ts +++ b/packages/contract-helpers/src/lendingPool-contract/index.ts @@ -46,7 +46,7 @@ import { SynthetixInterface, SynthetixService } from '../synthetix-contract'; import { WETHGatewayInterface, WETHGatewayService, -} from '../v3-wethgateway-contract'; +} from '../wethgateway-contract'; import { LPBorrowParamsType, LPDepositParamsType, @@ -367,6 +367,7 @@ export class LendingPool user, amount, debtTokenAddress, + interestRateMode, referralCode, }); } @@ -420,6 +421,7 @@ export class LendingPool lendingPool: this.lendingPoolAddress, user, amount, + interestRateMode, onBehalfOf, }); } diff --git a/packages/contract-helpers/src/wethgateway-contract/index.ts b/packages/contract-helpers/src/wethgateway-contract/index.ts new file mode 100644 index 000000000..d3c262c9c --- /dev/null +++ b/packages/contract-helpers/src/wethgateway-contract/index.ts @@ -0,0 +1,408 @@ +import { BigNumber, constants, PopulatedTransaction, providers } from 'ethers'; +import { + BaseDebtToken, + BaseDebtTokenInterface, +} from '../baseDebtToken-contract'; +import BaseService from '../commons/BaseService'; +import { + eEthereumTxType, + EthereumTransactionTypeExtended, + InterestRate, + ProtocolAction, + tEthereumAddress, + transactionType, +} from '../commons/types'; +import { gasLimitRecommendations, valueToWei } from '../commons/utils'; +import { WETHValidator } from '../commons/validators/methodValidators'; +import { + is0OrPositiveAmount, + isEthAddress, + isPositiveAmount, + isPositiveOrMinusOneAmount, +} from '../commons/validators/paramValidators'; +import { IERC20ServiceInterface } from '../erc20-contract'; +import { IWETHGateway, IWETHGatewayInterface } from './typechain/IWETHGateway'; +import { IWETHGateway__factory } from './typechain/IWETHGateway__factory'; + +export type WETHDepositParamsType = { + lendingPool: tEthereumAddress; + user: tEthereumAddress; + amount: string; // normal + onBehalfOf?: tEthereumAddress; + referralCode?: string; +}; + +export type WETHWithdrawParamsType = { + lendingPool: tEthereumAddress; + user: tEthereumAddress; + amount: string; + aTokenAddress: tEthereumAddress; + onBehalfOf?: tEthereumAddress; +}; + +export type WETHRepayParamsType = { + lendingPool: tEthereumAddress; + user: tEthereumAddress; + amount: string; + interestRateMode: InterestRate; + onBehalfOf?: tEthereumAddress; +}; + +export type WETHBorrowParamsType = { + lendingPool: tEthereumAddress; + user: tEthereumAddress; + amount: string; + debtTokenAddress?: tEthereumAddress; + interestRateMode: InterestRate; + referralCode?: string; +}; + +export interface WETHGatewayInterface { + generateDepositEthTxData: ( + args: WETHDepositParamsType, + ) => PopulatedTransaction; + generateBorrowEthTxData: (args: WETHBorrowParamsType) => PopulatedTransaction; + generateRepayEthTxData: (args: WETHRepayParamsType) => PopulatedTransaction; + depositETH: ( + args: WETHDepositParamsType, + ) => EthereumTransactionTypeExtended[]; + withdrawETH: ( + args: WETHWithdrawParamsType, + ) => Promise; + repayETH: (args: WETHRepayParamsType) => EthereumTransactionTypeExtended[]; + borrowETH: ( + args: WETHBorrowParamsType, + ) => Promise; +} + +export class WETHGatewayService + extends BaseService + implements WETHGatewayInterface +{ + readonly wethGatewayAddress: string; + + readonly baseDebtTokenService: BaseDebtTokenInterface; + + readonly erc20Service: IERC20ServiceInterface; + + readonly wethGatewayInstance: IWETHGatewayInterface; + + generateDepositEthTxData: ( + args: WETHDepositParamsType, + ) => PopulatedTransaction; + + generateBorrowEthTxData: (args: WETHBorrowParamsType) => PopulatedTransaction; + + generateRepayEthTxData: (args: WETHRepayParamsType) => PopulatedTransaction; + + constructor( + provider: providers.Provider, + erc20Service: IERC20ServiceInterface, + wethGatewayAddress?: string, + ) { + super(provider, IWETHGateway__factory); + this.erc20Service = erc20Service; + + this.baseDebtTokenService = new BaseDebtToken( + this.provider, + this.erc20Service, + ); + + this.wethGatewayAddress = wethGatewayAddress ?? ''; + + this.depositETH = this.depositETH.bind(this); + this.withdrawETH = this.withdrawETH.bind(this); + this.repayETH = this.repayETH.bind(this); + this.borrowETH = this.borrowETH.bind(this); + this.wethGatewayInstance = IWETHGateway__factory.createInterface(); + this.generateDepositEthTxData = ( + args: WETHDepositParamsType, + ): PopulatedTransaction => { + const txData = this.wethGatewayInstance.encodeFunctionData('depositETH', [ + args.lendingPool, + args.onBehalfOf ?? args.user, + args.referralCode ?? '0', + ]); + const actionTx: PopulatedTransaction = { + data: txData, + to: this.wethGatewayAddress, + from: args.user, + value: BigNumber.from(args.amount), + gasLimit: BigNumber.from( + gasLimitRecommendations[ProtocolAction.deposit].limit, + ), + }; + return actionTx; + }; + + this.generateBorrowEthTxData = ( + args: WETHBorrowParamsType, + ): PopulatedTransaction => { + const numericRateMode = + args.interestRateMode === InterestRate.Variable ? 2 : 1; + const txData = this.wethGatewayInstance.encodeFunctionData('borrowETH', [ + args.lendingPool, + args.amount, + numericRateMode, + args.referralCode ?? '0', + ]); + const actionTx: PopulatedTransaction = { + data: txData, + to: this.wethGatewayAddress, + from: args.user, + gasLimit: BigNumber.from( + gasLimitRecommendations[ProtocolAction.borrowETH].limit, + ), + }; + return actionTx; + }; + + this.generateRepayEthTxData = ({ + interestRateMode, + lendingPool, + amount, + user, + onBehalfOf, + }) => { + const numericRateMode = + interestRateMode === InterestRate.Variable ? 2 : 1; + const txData = this.wethGatewayInstance.encodeFunctionData('repayETH', [ + lendingPool, + amount, + numericRateMode, + onBehalfOf ?? user, + ]); + const actionTx: PopulatedTransaction = { + data: txData, + to: this.wethGatewayAddress, + from: user, + value: BigNumber.from(amount), + gasLimit: BigNumber.from( + gasLimitRecommendations[ProtocolAction.repayETH].limit, + ), + }; + return actionTx; + }; + } + + @WETHValidator + public depositETH( + @isEthAddress('lendingPool') + @isEthAddress('user') + @isEthAddress('onBehalfOf') + @isPositiveAmount('amount') + @is0OrPositiveAmount('referralCode') + { + lendingPool, + user, + amount, + onBehalfOf, + referralCode, + }: WETHDepositParamsType, + ): EthereumTransactionTypeExtended[] { + const convertedAmount: string = valueToWei(amount, 18); + + const wethGatewayContract: IWETHGateway = this.getContractInstance( + this.wethGatewayAddress, + ); + const txCallback: () => Promise = this.generateTxCallback({ + rawTxMethod: async () => + wethGatewayContract.populateTransaction.depositETH( + lendingPool, + onBehalfOf ?? user, + referralCode ?? '0', + ), + from: user, + value: convertedAmount, + }); + + return [ + { + tx: txCallback, + txType: eEthereumTxType.DLP_ACTION, + gas: this.generateTxPriceEstimation([], txCallback), + }, + ]; + } + + @WETHValidator + public async borrowETH( + @isEthAddress('lendingPool') + @isEthAddress('user') + @isPositiveAmount('amount') + @isEthAddress('debtTokenAddress') + @is0OrPositiveAmount('referralCode') + { + lendingPool, + user, + amount, + debtTokenAddress, + interestRateMode, + referralCode, + }: WETHBorrowParamsType, + ): Promise { + const txs: EthereumTransactionTypeExtended[] = []; + const convertedAmount: string = valueToWei(amount, 18); + const numericRateMode = interestRateMode === InterestRate.Variable ? 2 : 1; + if (!debtTokenAddress) { + throw new Error( + `To borrow ETH you need to pass the stable or variable WETH debt Token Address corresponding the interestRateMode`, + ); + } + + const delegationApproved: boolean = + await this.baseDebtTokenService.isDelegationApproved({ + debtTokenAddress, + allowanceGiver: user, + allowanceReceiver: this.wethGatewayAddress, + amount, + }); + + if (!delegationApproved) { + const approveDelegationTx: EthereumTransactionTypeExtended = + this.baseDebtTokenService.approveDelegation({ + user, + delegatee: this.wethGatewayAddress, + debtTokenAddress, + amount: constants.MaxUint256.toString(), + }); + + txs.push(approveDelegationTx); + } + + const wethGatewayContract: IWETHGateway = this.getContractInstance( + this.wethGatewayAddress, + ); + + const txCallback: () => Promise = this.generateTxCallback({ + rawTxMethod: async () => + wethGatewayContract.populateTransaction.borrowETH( + lendingPool, + convertedAmount, + numericRateMode, + referralCode ?? '0', + ), + from: user, + }); + + txs.push({ + tx: txCallback, + txType: eEthereumTxType.DLP_ACTION, + gas: this.generateTxPriceEstimation( + txs, + txCallback, + ProtocolAction.borrowETH, + ), + }); + + return txs; + } + + @WETHValidator + public async withdrawETH( + @isEthAddress('lendingPool') + @isEthAddress('user') + @isEthAddress('onBehalfOf') + @isPositiveOrMinusOneAmount('amount') + @isEthAddress('aTokenAddress') + { + lendingPool, + user, + amount, + onBehalfOf, + aTokenAddress, + }: WETHWithdrawParamsType, + ): Promise { + const txs: EthereumTransactionTypeExtended[] = []; + const { isApproved, approve }: IERC20ServiceInterface = this.erc20Service; + const convertedAmount: string = + amount === '-1' + ? constants.MaxUint256.toString() + : valueToWei(amount, 18); + + const approved: boolean = await isApproved({ + token: aTokenAddress, + user, + spender: this.wethGatewayAddress, + amount, + }); + + if (!approved) { + const approveTx: EthereumTransactionTypeExtended = approve({ + user, + token: aTokenAddress, + spender: this.wethGatewayAddress, + amount: constants.MaxUint256.toString(), + }); + txs.push(approveTx); + } + + const wethGatewayContract: IWETHGateway = this.getContractInstance( + this.wethGatewayAddress, + ); + + const txCallback: () => Promise = this.generateTxCallback({ + rawTxMethod: async () => + wethGatewayContract.populateTransaction.withdrawETH( + lendingPool, + convertedAmount, + onBehalfOf ?? user, + ), + from: user, + }); + + txs.push({ + tx: txCallback, + txType: eEthereumTxType.DLP_ACTION, + gas: this.generateTxPriceEstimation( + txs, + txCallback, + ProtocolAction.withdrawETH, + ), + }); + + return txs; + } + + @WETHValidator + public repayETH( + @isEthAddress('lendingPool') + @isEthAddress('user') + @isEthAddress('onBehalfOf') + @isPositiveAmount('amount') + { + lendingPool, + user, + amount, + interestRateMode, + onBehalfOf, + }: WETHRepayParamsType, + ): EthereumTransactionTypeExtended[] { + const convertedAmount: string = valueToWei(amount, 18); + const numericRateMode = interestRateMode === InterestRate.Variable ? 2 : 1; + const wethGatewayContract: IWETHGateway = this.getContractInstance( + this.wethGatewayAddress, + ); + + const txCallback: () => Promise = this.generateTxCallback({ + rawTxMethod: async () => + wethGatewayContract.populateTransaction.repayETH( + lendingPool, + convertedAmount, + numericRateMode, + onBehalfOf ?? user, + ), + gasSurplus: 30, + from: user, + value: convertedAmount, + }); + + return [ + { + tx: txCallback, + txType: eEthereumTxType.DLP_ACTION, + gas: this.generateTxPriceEstimation([], txCallback), + }, + ]; + } +} diff --git a/packages/contract-helpers/src/wethgateway-contract/typechain/IWETHGateway.d.ts b/packages/contract-helpers/src/wethgateway-contract/typechain/IWETHGateway.d.ts new file mode 100644 index 000000000..5940f4dff --- /dev/null +++ b/packages/contract-helpers/src/wethgateway-contract/typechain/IWETHGateway.d.ts @@ -0,0 +1,381 @@ +/* Autogenerated file. Do not edit manually. */ +/* eslint-disable */ + +import { + ethers, + EventFilter, + Signer, + BigNumber, + BigNumberish, + PopulatedTransaction, +} from 'ethers'; +import { + Contract, + ContractTransaction, + Overrides, + PayableOverrides, + CallOverrides, +} from '@ethersproject/contracts'; +import { BytesLike } from '@ethersproject/bytes'; +import { Listener, Provider } from '@ethersproject/providers'; +import { FunctionFragment, EventFragment, Result } from '@ethersproject/abi'; + +interface IWETHGatewayInterface extends ethers.utils.Interface { + functions: { + 'borrowETH(address,uint256,uint256,uint16)': FunctionFragment; + 'depositETH(address,address,uint16)': FunctionFragment; + 'repayETH(address,uint256,uint256,address)': FunctionFragment; + 'withdrawETH(address,uint256,address)': FunctionFragment; + }; + + encodeFunctionData( + functionFragment: 'borrowETH', + values: [string, BigNumberish, BigNumberish, BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'depositETH', + values: [string, string, BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'repayETH', + values: [string, BigNumberish, BigNumberish, string], + ): string; + encodeFunctionData( + functionFragment: 'withdrawETH', + values: [string, BigNumberish, string], + ): string; + + decodeFunctionResult(functionFragment: 'borrowETH', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'depositETH', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'repayETH', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'withdrawETH', + data: BytesLike, + ): Result; + + events: {}; +} + +export class IWETHGateway extends Contract { + connect(signerOrProvider: Signer | Provider | string): this; + attach(addressOrName: string): this; + deployed(): Promise; + + on(event: EventFilter | string, listener: Listener): this; + once(event: EventFilter | string, listener: Listener): this; + addListener(eventName: EventFilter | string, listener: Listener): this; + removeAllListeners(eventName: EventFilter | string): this; + removeListener(eventName: any, listener: Listener): this; + + interface: IWETHGatewayInterface; + + functions: { + borrowETH( + lendingPool: string, + amount: BigNumberish, + interesRateMode: BigNumberish, + referralCode: BigNumberish, + overrides?: Overrides, + ): Promise; + + 'borrowETH(address,uint256,uint256,uint16)'( + lendingPool: string, + amount: BigNumberish, + interesRateMode: BigNumberish, + referralCode: BigNumberish, + overrides?: Overrides, + ): Promise; + + depositETH( + lendingPool: string, + onBehalfOf: string, + referralCode: BigNumberish, + overrides?: PayableOverrides, + ): Promise; + + 'depositETH(address,address,uint16)'( + lendingPool: string, + onBehalfOf: string, + referralCode: BigNumberish, + overrides?: PayableOverrides, + ): Promise; + + repayETH( + lendingPool: string, + amount: BigNumberish, + rateMode: BigNumberish, + onBehalfOf: string, + overrides?: PayableOverrides, + ): Promise; + + 'repayETH(address,uint256,uint256,address)'( + lendingPool: string, + amount: BigNumberish, + rateMode: BigNumberish, + onBehalfOf: string, + overrides?: PayableOverrides, + ): Promise; + + withdrawETH( + lendingPool: string, + amount: BigNumberish, + onBehalfOf: string, + overrides?: Overrides, + ): Promise; + + 'withdrawETH(address,uint256,address)'( + lendingPool: string, + amount: BigNumberish, + onBehalfOf: string, + overrides?: Overrides, + ): Promise; + }; + + borrowETH( + lendingPool: string, + amount: BigNumberish, + interesRateMode: BigNumberish, + referralCode: BigNumberish, + overrides?: Overrides, + ): Promise; + + 'borrowETH(address,uint256,uint256,uint16)'( + lendingPool: string, + amount: BigNumberish, + interesRateMode: BigNumberish, + referralCode: BigNumberish, + overrides?: Overrides, + ): Promise; + + depositETH( + lendingPool: string, + onBehalfOf: string, + referralCode: BigNumberish, + overrides?: PayableOverrides, + ): Promise; + + 'depositETH(address,address,uint16)'( + lendingPool: string, + onBehalfOf: string, + referralCode: BigNumberish, + overrides?: PayableOverrides, + ): Promise; + + repayETH( + lendingPool: string, + amount: BigNumberish, + rateMode: BigNumberish, + onBehalfOf: string, + overrides?: PayableOverrides, + ): Promise; + + 'repayETH(address,uint256,uint256,address)'( + lendingPool: string, + amount: BigNumberish, + rateMode: BigNumberish, + onBehalfOf: string, + overrides?: PayableOverrides, + ): Promise; + + withdrawETH( + lendingPool: string, + amount: BigNumberish, + onBehalfOf: string, + overrides?: Overrides, + ): Promise; + + 'withdrawETH(address,uint256,address)'( + lendingPool: string, + amount: BigNumberish, + onBehalfOf: string, + overrides?: Overrides, + ): Promise; + + callStatic: { + borrowETH( + lendingPool: string, + amount: BigNumberish, + interesRateMode: BigNumberish, + referralCode: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + 'borrowETH(address,uint256,uint256,uint16)'( + lendingPool: string, + amount: BigNumberish, + interesRateMode: BigNumberish, + referralCode: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + depositETH( + lendingPool: string, + onBehalfOf: string, + referralCode: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + 'depositETH(address,address,uint16)'( + lendingPool: string, + onBehalfOf: string, + referralCode: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + repayETH( + lendingPool: string, + amount: BigNumberish, + rateMode: BigNumberish, + onBehalfOf: string, + overrides?: CallOverrides, + ): Promise; + + 'repayETH(address,uint256,uint256,address)'( + lendingPool: string, + amount: BigNumberish, + rateMode: BigNumberish, + onBehalfOf: string, + overrides?: CallOverrides, + ): Promise; + + withdrawETH( + lendingPool: string, + amount: BigNumberish, + onBehalfOf: string, + overrides?: CallOverrides, + ): Promise; + + 'withdrawETH(address,uint256,address)'( + lendingPool: string, + amount: BigNumberish, + onBehalfOf: string, + overrides?: CallOverrides, + ): Promise; + }; + + filters: {}; + + estimateGas: { + borrowETH( + lendingPool: string, + amount: BigNumberish, + interesRateMode: BigNumberish, + referralCode: BigNumberish, + overrides?: Overrides, + ): Promise; + + 'borrowETH(address,uint256,uint256,uint16)'( + lendingPool: string, + amount: BigNumberish, + interesRateMode: BigNumberish, + referralCode: BigNumberish, + overrides?: Overrides, + ): Promise; + + depositETH( + lendingPool: string, + onBehalfOf: string, + referralCode: BigNumberish, + overrides?: PayableOverrides, + ): Promise; + + 'depositETH(address,address,uint16)'( + lendingPool: string, + onBehalfOf: string, + referralCode: BigNumberish, + overrides?: PayableOverrides, + ): Promise; + + repayETH( + lendingPool: string, + amount: BigNumberish, + rateMode: BigNumberish, + onBehalfOf: string, + overrides?: PayableOverrides, + ): Promise; + + 'repayETH(address,uint256,uint256,address)'( + lendingPool: string, + amount: BigNumberish, + rateMode: BigNumberish, + onBehalfOf: string, + overrides?: PayableOverrides, + ): Promise; + + withdrawETH( + lendingPool: string, + amount: BigNumberish, + onBehalfOf: string, + overrides?: Overrides, + ): Promise; + + 'withdrawETH(address,uint256,address)'( + lendingPool: string, + amount: BigNumberish, + onBehalfOf: string, + overrides?: Overrides, + ): Promise; + }; + + populateTransaction: { + borrowETH( + lendingPool: string, + amount: BigNumberish, + interesRateMode: BigNumberish, + referralCode: BigNumberish, + overrides?: Overrides, + ): Promise; + + 'borrowETH(address,uint256,uint256,uint16)'( + lendingPool: string, + amount: BigNumberish, + interesRateMode: BigNumberish, + referralCode: BigNumberish, + overrides?: Overrides, + ): Promise; + + depositETH( + lendingPool: string, + onBehalfOf: string, + referralCode: BigNumberish, + overrides?: PayableOverrides, + ): Promise; + + 'depositETH(address,address,uint16)'( + lendingPool: string, + onBehalfOf: string, + referralCode: BigNumberish, + overrides?: PayableOverrides, + ): Promise; + + repayETH( + lendingPool: string, + amount: BigNumberish, + rateMode: BigNumberish, + onBehalfOf: string, + overrides?: PayableOverrides, + ): Promise; + + 'repayETH(address,uint256,uint256,address)'( + lendingPool: string, + amount: BigNumberish, + rateMode: BigNumberish, + onBehalfOf: string, + overrides?: PayableOverrides, + ): Promise; + + withdrawETH( + lendingPool: string, + amount: BigNumberish, + onBehalfOf: string, + overrides?: Overrides, + ): Promise; + + 'withdrawETH(address,uint256,address)'( + lendingPool: string, + amount: BigNumberish, + onBehalfOf: string, + overrides?: Overrides, + ): Promise; + }; +} diff --git a/packages/contract-helpers/src/wethgateway-contract/typechain/IWETHGateway__factory.ts b/packages/contract-helpers/src/wethgateway-contract/typechain/IWETHGateway__factory.ts new file mode 100644 index 000000000..54d978518 --- /dev/null +++ b/packages/contract-helpers/src/wethgateway-contract/typechain/IWETHGateway__factory.ts @@ -0,0 +1,124 @@ +/* Autogenerated file. Do not edit manually. */ +/* eslint-disable */ + +import { Contract, Signer, utils } from 'ethers'; +import { Provider } from '@ethersproject/providers'; + +import type { IWETHGateway, IWETHGatewayInterface } from './IWETHGateway'; + +export class IWETHGateway__factory { + static connect( + address: string, + signerOrProvider: Signer | Provider, + ): IWETHGateway { + return new Contract(address, _abi, signerOrProvider) as IWETHGateway; + } + static createInterface(): IWETHGatewayInterface { + return new utils.Interface(_abi) as IWETHGatewayInterface; + } +} + +const _abi = [ + { + inputs: [ + { + internalType: 'address', + name: 'lendingPool', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'interesRateMode', + type: 'uint256', + }, + { + internalType: 'uint16', + name: 'referralCode', + type: 'uint16', + }, + ], + name: 'borrowETH', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'lendingPool', + type: 'address', + }, + { + internalType: 'address', + name: 'onBehalfOf', + type: 'address', + }, + { + internalType: 'uint16', + name: 'referralCode', + type: 'uint16', + }, + ], + name: 'depositETH', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'lendingPool', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'rateMode', + type: 'uint256', + }, + { + internalType: 'address', + name: 'onBehalfOf', + type: 'address', + }, + ], + name: 'repayETH', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'lendingPool', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'address', + name: 'onBehalfOf', + type: 'address', + }, + ], + name: 'withdrawETH', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +]; diff --git a/packages/contract-helpers/src/wethgateway-contract/wethGateway.test.ts b/packages/contract-helpers/src/wethgateway-contract/wethGateway.test.ts new file mode 100644 index 000000000..5b6ef6db2 --- /dev/null +++ b/packages/contract-helpers/src/wethgateway-contract/wethGateway.test.ts @@ -0,0 +1,1298 @@ +import { BigNumber, constants, providers, utils } from 'ethers'; +import { + eEthereumTxType, + GasType, + InterestRate, + transactionType, +} from '../commons/types'; +import { valueToWei } from '../commons/utils'; +import { ERC20Service } from '../erc20-contract'; +import { WETHGatewayService } from './index'; + +jest.mock('../commons/gasStation', () => { + return { + __esModule: true, + estimateGasByNetwork: jest + .fn() + .mockImplementation(async () => Promise.resolve(BigNumber.from(1))), + estimateGas: jest.fn(async () => Promise.resolve(BigNumber.from(1))), + }; +}); + +describe('WethGatewayService', () => { + const wethGatewayAddress = '0x0000000000000000000000000000000000000001'; + const lendingPool = '0x0000000000000000000000000000000000000002'; + describe('Initialization', () => { + const provider: providers.Provider = new providers.JsonRpcProvider(); + const erc20Service = new ERC20Service(provider); + it('Expects to be initialized', () => { + expect( + () => + new WETHGatewayService(provider, erc20Service, wethGatewayAddress), + ).not.toThrow(); + }); + it('Expects to initialize without wethgateway address', () => { + expect( + () => new WETHGatewayService(provider, erc20Service), + ).not.toThrow(); + }); + }); + describe('generateDepositEthTxData', () => { + it('generates depositETH tx data', () => { + const provider: providers.Provider = new providers.JsonRpcProvider(); + const erc20Service = new ERC20Service(provider); + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const user = '0x0000000000000000000000000000000000000003'; + const txData = weth.generateDepositEthTxData({ + lendingPool, + user, + amount: '1', + }); + + expect(txData.to).toEqual(wethGatewayAddress); + expect(txData.from).toEqual(user); + expect(txData.data).toEqual( + '0x474cf53d000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000', + ); + + const onBehalfOf = '0x0000000000000000000000000000000000000004'; + + const txDataUpdatedParams = weth.generateDepositEthTxData({ + lendingPool, + user, + amount: '1', + onBehalfOf, + referralCode: '0', + }); + + expect(txDataUpdatedParams.to).toEqual(wethGatewayAddress); + expect(txDataUpdatedParams.from).toEqual(user); + expect(txDataUpdatedParams.data).toEqual( + '0x474cf53d000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000', + ); + }); + }); + describe('generateBorrowEthTxData', () => { + it('generates borrowETH tx data', async () => { + const provider: providers.Provider = new providers.JsonRpcProvider(); + const erc20Service = new ERC20Service(provider); + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const user = '0x0000000000000000000000000000000000000003'; + const txData = weth.generateBorrowEthTxData({ + lendingPool, + user, + amount: '1', + interestRateMode: InterestRate.Variable, + referralCode: '0', + }); + + expect(txData.to).toEqual(wethGatewayAddress); + expect(txData.from).toEqual(user); + expect(txData.data).toEqual( + '0x66514c970000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000', + ); + + const txDataStable = weth.generateBorrowEthTxData({ + lendingPool, + user, + amount: '1', + interestRateMode: InterestRate.Stable, + debtTokenAddress: '', + }); + + expect(txDataStable.data).toEqual( + '0x66514c970000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000', + ); + }); + }); + describe('generateRepayEthTxData', () => { + it('generates repayETH tx data with variable debt', () => { + const provider: providers.Provider = new providers.JsonRpcProvider(); + const erc20Service = new ERC20Service(provider); + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const user = '0x0000000000000000000000000000000000000003'; + const txData = weth.generateRepayEthTxData({ + lendingPool, + user, + amount: '1', + interestRateMode: InterestRate.Variable, + }); + + expect(txData.to).toEqual(wethGatewayAddress); + expect(txData.from).toEqual(user); + expect(txData.data).toEqual( + '0x02c5fcf80000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003', + ); + + const onBehalfOf = '0x0000000000000000000000000000000000000004'; + + const txDataUpdatedParams = weth.generateRepayEthTxData({ + lendingPool, + user, + amount: '1', + onBehalfOf, + interestRateMode: InterestRate.Variable, + }); + + expect(txDataUpdatedParams.to).toEqual(wethGatewayAddress); + expect(txDataUpdatedParams.from).toEqual(user); + expect(txDataUpdatedParams.data).toEqual( + '0x02c5fcf80000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004', + ); + }); + + it('generates repayETH tx data with stable debt', () => { + const provider: providers.Provider = new providers.JsonRpcProvider(); + const erc20Service = new ERC20Service(provider); + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const user = '0x0000000000000000000000000000000000000003'; + const txData = weth.generateRepayEthTxData({ + lendingPool, + user, + amount: '1', + interestRateMode: InterestRate.Stable, + }); + + expect(txData.to).toEqual(wethGatewayAddress); + expect(txData.from).toEqual(user); + expect(txData.data).toEqual( + '0x02c5fcf80000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003', + ); + }); + }); + describe('depositETH', () => { + const user = '0x0000000000000000000000000000000000000003'; + const onBehalfOf = '0x0000000000000000000000000000000000000004'; + const amount = '123.456'; + const referralCode = '0'; + const provider: providers.Provider = new providers.JsonRpcProvider(); + jest + .spyOn(provider, 'getGasPrice') + .mockImplementation(async () => Promise.resolve(BigNumber.from(1))); + const erc20Service = new ERC20Service(provider); + afterEach(() => { + jest.clearAllMocks(); + }); + + it('Expects the deposit tx object to be correct with all params', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const txObj = weth.depositETH({ + lendingPool, + user, + amount, + onBehalfOf, + referralCode, + }); + expect(txObj.length).toEqual(1); + expect(txObj[0].txType).toEqual(eEthereumTxType.DLP_ACTION); + + const tx: transactionType = await txObj[0].tx(); + expect(tx.to).toEqual(wethGatewayAddress); + expect(tx.from).toEqual(user); + expect(tx.gasLimit).toEqual(BigNumber.from(1)); + + const decoded = utils.defaultAbiCoder.decode( + ['address', 'address', 'uint16'], + utils.hexDataSlice(tx.data ?? '', 4), + ); + + expect(decoded[0]).toEqual(lendingPool); + expect(decoded[1]).toEqual(onBehalfOf); + expect(decoded[2]).toEqual(Number(referralCode)); + + // gas price + const gasPrice: GasType | null = await txObj[0].gas(); + expect(gasPrice).not.toBeNull(); + expect(gasPrice?.gasLimit).toEqual('1'); + expect(gasPrice?.gasPrice).toEqual('1'); + }); + it('Expects the deposit tx object to be correct without onBehalfOf', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const txObj = weth.depositETH({ + lendingPool, + user, + amount, + referralCode, + }); + expect(txObj.length).toEqual(1); + expect(txObj[0].txType).toEqual(eEthereumTxType.DLP_ACTION); + + const tx: transactionType = await txObj[0].tx(); + expect(tx.to).toEqual(wethGatewayAddress); + expect(tx.from).toEqual(user); + expect(tx.gasLimit).toEqual(BigNumber.from(1)); + + const decoded = utils.defaultAbiCoder.decode( + ['address', 'address', 'uint16'], + utils.hexDataSlice(tx.data ?? '', 4), + ); + + expect(decoded[0]).toEqual(lendingPool); + expect(decoded[1]).toEqual(user); + expect(decoded[2]).toEqual(Number(referralCode)); + + // gas price + const gasPrice: GasType | null = await txObj[0].gas(); + expect(gasPrice).not.toBeNull(); + expect(gasPrice?.gasLimit).toEqual('1'); + expect(gasPrice?.gasPrice).toEqual('1'); + }); + it('Expects the deposit tx object to be correct without referralCode', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const txObj = weth.depositETH({ + lendingPool, + user, + amount, + }); + expect(txObj.length).toEqual(1); + expect(txObj[0].txType).toEqual(eEthereumTxType.DLP_ACTION); + + const tx: transactionType = await txObj[0].tx(); + expect(tx.to).toEqual(wethGatewayAddress); + expect(tx.from).toEqual(user); + expect(tx.gasLimit).toEqual(BigNumber.from(1)); + + const decoded = utils.defaultAbiCoder.decode( + ['address', 'address', 'uint16'], + utils.hexDataSlice(tx.data ?? '', 4), + ); + + expect(decoded[0]).toEqual(lendingPool); + expect(decoded[1]).toEqual(user); + expect(decoded[2]).toEqual(0); + + // gas price + const gasPrice: GasType | null = await txObj[0].gas(); + expect(gasPrice).not.toBeNull(); + expect(gasPrice?.gasLimit).toEqual('1'); + expect(gasPrice?.gasPrice).toEqual('1'); + }); + it('Expects to fail when initialized without gateway address', () => { + const weth = new WETHGatewayService(provider, erc20Service); + + const txObj = weth.depositETH({ + lendingPool, + user, + amount, + }); + + expect(txObj.length).toEqual(0); + }); + it('Expects to fail when user is not address', () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const user = 'asdf'; + expect(() => + weth.depositETH({ + lendingPool, + user, + amount, + onBehalfOf, + referralCode, + }), + ).toThrowError(`Address: ${user} is not a valid ethereum Address`); + }); + it('Expects to fail when lendingPool is not address', () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const lendingPool = 'asdf'; + expect(() => + weth.depositETH({ + lendingPool, + user, + amount, + onBehalfOf, + referralCode, + }), + ).toThrowError(`Address: ${lendingPool} is not a valid ethereum Address`); + }); + it('Expects to fail when amount is not positive', () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const amount = '0'; + expect(() => + weth.depositETH({ + lendingPool, + user, + amount, + onBehalfOf, + referralCode, + }), + ).toThrowError(`Amount: ${amount} needs to be greater than 0`); + }); + it('Expects to fail when amount is not number', () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const amount = 'asdf'; + expect(() => + weth.depositETH({ + lendingPool, + user, + amount, + onBehalfOf, + referralCode, + }), + ).toThrowError(`Amount: ${amount} needs to be greater than 0`); + }); + it('Expects to fail when onBehalfOf is not address', () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const onBehalfOf = 'asdf'; + expect(() => + weth.depositETH({ + lendingPool, + user, + amount, + onBehalfOf, + referralCode, + }), + ).toThrowError(`Address: ${onBehalfOf} is not a valid ethereum Address`); + }); + it('Expects to fail when referral is not number', () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const referralCode = 'asdf'; + expect(() => + weth.depositETH({ + lendingPool, + user, + amount, + onBehalfOf, + referralCode, + }), + ).toThrowError( + `Amount: ${referralCode} needs to be greater or equal than 0`, + ); + }); + }); + describe('withdrawETH', () => { + const user = '0x0000000000000000000000000000000000000003'; + const debtTokenAddress = '0x0000000000000000000000000000000000000005'; + const interestRateMode = InterestRate.Stable; + const amount = '123.456'; + const referralCode = '0'; + const provider: providers.Provider = new providers.JsonRpcProvider(); + jest + .spyOn(provider, 'getGasPrice') + .mockImplementation(async () => Promise.resolve(BigNumber.from(1))); + const erc20Service = new ERC20Service(provider); + + afterEach(() => { + jest.clearAllMocks(); + }); + it('Expects the borrow tx object to be correct with all params and variable stable rate without approval', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + + const isApprovedSpy = jest + .spyOn(weth.baseDebtTokenService, 'isDelegationApproved') + .mockImplementation(async () => Promise.resolve(false)); + + const approveSpy = jest + .spyOn(weth.baseDebtTokenService, 'approveDelegation') + .mockImplementation(() => ({ + txType: eEthereumTxType.ERC20_APPROVAL, + tx: async () => ({}), + gas: async () => ({ + gasLimit: '1', + gasPrice: '1', + }), + })); + const interestRateMode = InterestRate.Variable; + const txObj = await weth.borrowETH({ + lendingPool, + user, + amount, + debtTokenAddress, + interestRateMode, + referralCode, + }); + + await expect(async () => + weth.borrowETH({ + lendingPool, + user, + amount, + interestRateMode, + referralCode, + }), + ).rejects.toThrowError( + `To borrow ETH you need to pass the stable or variable WETH debt Token Address corresponding the interestRateMode`, + ); + + expect(isApprovedSpy).toHaveBeenCalled(); + expect(approveSpy).toHaveBeenCalled(); + expect(txObj.length).toEqual(2); + expect(txObj[1].txType).toEqual(eEthereumTxType.DLP_ACTION); + + const tx: transactionType = await txObj[1].tx(); + expect(tx.to).toEqual(wethGatewayAddress); + expect(tx.from).toEqual(user); + expect(tx.gasLimit).toEqual(BigNumber.from(1)); + + const decoded = utils.defaultAbiCoder.decode( + ['address', 'uint256', 'uint256', 'uint16'], + utils.hexDataSlice(tx.data ?? '', 4), + ); + + expect(decoded[0]).toEqual(lendingPool); + expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); + expect(decoded[2]).toEqual(BigNumber.from(2)); + expect(decoded[3]).toEqual(Number(referralCode)); + + // gas price + const gasPrice: GasType | null = await txObj[1].gas(); + expect(gasPrice).not.toBeNull(); + expect(gasPrice?.gasLimit).toEqual('450000'); + expect(gasPrice?.gasPrice).toEqual('1'); + }); + it('Expects the borrow tx object to be correct with all params and stable stable rate already approved', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + + const isApprovedSpy = jest + .spyOn(weth.baseDebtTokenService, 'isDelegationApproved') + .mockImplementation(async () => Promise.resolve(true)); + + const txObj = await weth.borrowETH({ + lendingPool, + user, + amount, + debtTokenAddress, + interestRateMode, + referralCode, + }); + + expect(isApprovedSpy).toHaveBeenCalled(); + expect(txObj.length).toEqual(1); + expect(txObj[0].txType).toEqual(eEthereumTxType.DLP_ACTION); + + const tx: transactionType = await txObj[0].tx(); + expect(tx.to).toEqual(wethGatewayAddress); + expect(tx.from).toEqual(user); + expect(tx.gasLimit).toEqual(BigNumber.from(1)); + + const decoded = utils.defaultAbiCoder.decode( + ['address', 'uint256', 'uint256', 'uint16'], + utils.hexDataSlice(tx.data ?? '', 4), + ); + + expect(decoded[0]).toEqual(lendingPool); + expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); + expect(decoded[2]).toEqual(BigNumber.from(1)); + expect(decoded[3]).toEqual(Number(referralCode)); + + // gas price + const gasPrice: GasType | null = await txObj[0].gas(); + expect(gasPrice).not.toBeNull(); + expect(gasPrice?.gasLimit).toEqual('1'); + expect(gasPrice?.gasPrice).toEqual('1'); + }); + it('Expects the borrow tx object to be correct with all params and none stable rate', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + + const isApprovedSpy = jest + .spyOn(weth.baseDebtTokenService, 'isDelegationApproved') + .mockImplementation(async () => Promise.resolve(true)); + + const interestRateMode = InterestRate.None; + const txObj = await weth.borrowETH({ + lendingPool, + user, + amount, + debtTokenAddress, + interestRateMode, + referralCode, + }); + + expect(isApprovedSpy).toHaveBeenCalled(); + expect(txObj.length).toEqual(1); + expect(txObj[0].txType).toEqual(eEthereumTxType.DLP_ACTION); + + const tx: transactionType = await txObj[0].tx(); + expect(tx.to).toEqual(wethGatewayAddress); + expect(tx.from).toEqual(user); + expect(tx.gasLimit).toEqual(BigNumber.from(1)); + + const decoded = utils.defaultAbiCoder.decode( + ['address', 'uint256', 'uint256', 'uint16'], + utils.hexDataSlice(tx.data ?? '', 4), + ); + + expect(decoded[0]).toEqual(lendingPool); + expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); + expect(decoded[2]).toEqual(BigNumber.from(1)); + expect(decoded[3]).toEqual(Number(referralCode)); + + // gas price + const gasPrice: GasType | null = await txObj[0].gas(); + expect(gasPrice).not.toBeNull(); + expect(gasPrice?.gasLimit).toEqual('1'); + expect(gasPrice?.gasPrice).toEqual('1'); + }); + it('Expects the borrow tx object to be correct without referralCode', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + + const isApprovedSpy = jest + .spyOn(weth.baseDebtTokenService, 'isDelegationApproved') + .mockImplementation(async () => Promise.resolve(true)); + + const interestRateMode = InterestRate.None; + const txObj = await weth.borrowETH({ + lendingPool, + user, + amount, + debtTokenAddress, + interestRateMode, + }); + + expect(isApprovedSpy).toHaveBeenCalled(); + expect(txObj.length).toEqual(1); + expect(txObj[0].txType).toEqual(eEthereumTxType.DLP_ACTION); + + const tx: transactionType = await txObj[0].tx(); + expect(tx.to).toEqual(wethGatewayAddress); + expect(tx.from).toEqual(user); + expect(tx.gasLimit).toEqual(BigNumber.from(1)); + + const decoded = utils.defaultAbiCoder.decode( + ['address', 'uint256', 'uint256', 'uint16'], + utils.hexDataSlice(tx.data ?? '', 4), + ); + + expect(decoded[0]).toEqual(lendingPool); + expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); + expect(decoded[2]).toEqual(BigNumber.from(1)); + expect(decoded[3]).toEqual(0); + + // gas price + const gasPrice: GasType | null = await txObj[0].gas(); + expect(gasPrice).not.toBeNull(); + expect(gasPrice?.gasLimit).toEqual('1'); + expect(gasPrice?.gasPrice).toEqual('1'); + }); + it('Expects to fail when initialized without gateway address', async () => { + const weth = new WETHGatewayService(provider, erc20Service); + + const txObj = await weth.borrowETH({ + lendingPool, + user, + amount, + debtTokenAddress, + interestRateMode, + referralCode, + }); + + expect(txObj.length).toEqual(0); + }); + it('Expects to fail when user is not address', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const user = 'asdf'; + await expect(async () => + weth.borrowETH({ + lendingPool, + user, + amount, + debtTokenAddress, + interestRateMode, + referralCode, + }), + ).rejects.toThrowError( + `Address: ${user} is not a valid ethereum Address`, + ); + }); + it('Expects to fail when lendingPool is not address', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const lendingPool = 'asdf'; + await expect(async () => + weth.borrowETH({ + lendingPool, + user, + amount, + debtTokenAddress, + interestRateMode, + referralCode, + }), + ).rejects.toThrowError( + `Address: ${lendingPool} is not a valid ethereum Address`, + ); + }); + it('Expects to fail when amount is not positive', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const amount = '0'; + await expect(async () => + weth.borrowETH({ + lendingPool, + user, + amount, + debtTokenAddress, + interestRateMode, + referralCode, + }), + ).rejects.toThrowError(`Amount: ${amount} needs to be greater than 0`); + }); + it('Expects to fail when amount is not number', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const amount = 'asdf'; + await expect(async () => + weth.borrowETH({ + lendingPool, + user, + amount, + debtTokenAddress, + interestRateMode, + referralCode, + }), + ).rejects.toThrowError(`Amount: ${amount} needs to be greater than 0`); + }); + it('Expects to fail when debtTokenAddress is not address', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const debtTokenAddress = 'asdf'; + await expect(async () => + weth.borrowETH({ + lendingPool, + user, + amount, + debtTokenAddress, + interestRateMode, + referralCode, + }), + ).rejects.toThrowError( + `Address: ${debtTokenAddress} is not a valid ethereum Address`, + ); + }); + it('Expects to fail when referral is not number', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const referralCode = 'asdf'; + await expect(async () => + weth.borrowETH({ + lendingPool, + user, + amount, + debtTokenAddress, + interestRateMode, + referralCode, + }), + ).rejects.toThrowError( + `Amount: ${referralCode} needs to be greater or equal than 0`, + ); + }); + }); + describe('repayETH', () => { + const user = '0x0000000000000000000000000000000000000003'; + const onBehalfOf = '0x0000000000000000000000000000000000000004'; + const aTokenAddress = '0x0000000000000000000000000000000000000005'; + const amount = '123.456'; + const provider: providers.Provider = new providers.JsonRpcProvider(); + jest + .spyOn(provider, 'getGasPrice') + .mockImplementation(async () => Promise.resolve(BigNumber.from(1))); + const erc20Service = new ERC20Service(provider); + + afterEach(() => { + jest.clearAllMocks(); + }); + it('Expects the withdraw tx object to be correct with all params and not approved', async () => { + const isApprovedSpy = jest + .spyOn(erc20Service, 'isApproved') + .mockImplementation(async () => Promise.resolve(false)); + const approveSpy = jest + .spyOn(erc20Service, 'approve') + .mockImplementation(() => ({ + txType: eEthereumTxType.ERC20_APPROVAL, + tx: async () => ({}), + gas: async () => ({ + gasLimit: '1', + gasPrice: '1', + }), + })); + + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const txObj = await weth.withdrawETH({ + lendingPool, + user, + amount, + onBehalfOf, + aTokenAddress, + }); + + expect(isApprovedSpy).toHaveBeenCalled(); + expect(approveSpy).toHaveBeenCalled(); + expect(txObj.length).toEqual(2); + expect(txObj[1].txType).toEqual(eEthereumTxType.DLP_ACTION); + + const tx: transactionType = await txObj[1].tx(); + expect(tx.to).toEqual(wethGatewayAddress); + expect(tx.from).toEqual(user); + expect(tx.gasLimit).toEqual(BigNumber.from(1)); + + const decoded = utils.defaultAbiCoder.decode( + ['address', 'uint256', 'address'], + utils.hexDataSlice(tx.data ?? '', 4), + ); + + expect(decoded[0]).toEqual(lendingPool); + expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); + expect(decoded[2]).toEqual(onBehalfOf); + + // gas price + const gasPrice: GasType | null = await txObj[1].gas(); + expect(gasPrice).not.toBeNull(); + expect(gasPrice?.gasLimit).toEqual('640000'); + expect(gasPrice?.gasPrice).toEqual('1'); + }); + it('Expects the withdraw tx object to be correct with all params and amount -1 and approved', async () => { + const isApprovedSpy = jest + .spyOn(erc20Service, 'isApproved') + .mockImplementation(async () => Promise.resolve(true)); + + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const amount = '-1'; + const txObj = await weth.withdrawETH({ + lendingPool, + user, + amount, + onBehalfOf, + aTokenAddress, + }); + + expect(isApprovedSpy).toHaveBeenCalled(); + expect(txObj.length).toEqual(1); + expect(txObj[0].txType).toEqual(eEthereumTxType.DLP_ACTION); + + const tx: transactionType = await txObj[0].tx(); + expect(tx.to).toEqual(wethGatewayAddress); + expect(tx.from).toEqual(user); + expect(tx.gasLimit).toEqual(BigNumber.from(1)); + + const decoded = utils.defaultAbiCoder.decode( + ['address', 'uint256', 'address'], + utils.hexDataSlice(tx.data ?? '', 4), + ); + + expect(decoded[0]).toEqual(lendingPool); + expect(decoded[1]).toEqual(constants.MaxUint256); + expect(decoded[2]).toEqual(onBehalfOf); + + // gas price + const gasPrice: GasType | null = await txObj[0].gas(); + expect(gasPrice).not.toBeNull(); + expect(gasPrice?.gasLimit).toEqual('1'); + expect(gasPrice?.gasPrice).toEqual('1'); + }); + it('Expects the withdraw tx object to be correct without onBehalfOf', async () => { + const isApprovedSpy = jest + .spyOn(erc20Service, 'isApproved') + .mockImplementation(async () => Promise.resolve(true)); + + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + + const txObj = await weth.withdrawETH({ + lendingPool, + user, + amount, + aTokenAddress, + }); + + expect(isApprovedSpy).toHaveBeenCalled(); + expect(txObj.length).toEqual(1); + expect(txObj[0].txType).toEqual(eEthereumTxType.DLP_ACTION); + + const tx: transactionType = await txObj[0].tx(); + expect(tx.to).toEqual(wethGatewayAddress); + expect(tx.from).toEqual(user); + expect(tx.gasLimit).toEqual(BigNumber.from(1)); + + const decoded = utils.defaultAbiCoder.decode( + ['address', 'uint256', 'address'], + utils.hexDataSlice(tx.data ?? '', 4), + ); + + expect(decoded[0]).toEqual(lendingPool); + expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); + expect(decoded[2]).toEqual(user); + + // gas price + const gasPrice: GasType | null = await txObj[0].gas(); + expect(gasPrice).not.toBeNull(); + expect(gasPrice?.gasLimit).toEqual('1'); + expect(gasPrice?.gasPrice).toEqual('1'); + }); + it('Expects to fail when initialized without gateway address', async () => { + const weth = new WETHGatewayService(provider, erc20Service); + + const txObj = await weth.withdrawETH({ + lendingPool, + user, + amount, + aTokenAddress, + }); + + expect(txObj.length).toEqual(0); + }); + it('Expects to fail when user is not address', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const user = 'asdf'; + await expect(async () => + weth.withdrawETH({ + lendingPool, + user, + amount, + aTokenAddress, + }), + ).rejects.toThrowError( + `Address: ${user} is not a valid ethereum Address`, + ); + }); + it('Expects to fail when lendingPool is not address', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const lendingPool = 'asdf'; + await expect(async () => + weth.withdrawETH({ + lendingPool, + user, + amount, + aTokenAddress, + }), + ).rejects.toThrowError( + `Address: ${lendingPool} is not a valid ethereum Address`, + ); + }); + it('Expects to fail when amount is not positive', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const amount = '0'; + await expect(async () => + weth.withdrawETH({ + lendingPool, + user, + amount, + aTokenAddress, + }), + ).rejects.toThrowError(`Amount: ${amount} needs to be greater than 0`); + }); + it('Expects to fail when amount is not number', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const amount = 'asdf'; + await expect(async () => + weth.withdrawETH({ + lendingPool, + user, + amount, + aTokenAddress, + }), + ).rejects.toThrowError(`Amount: ${amount} needs to be greater than 0`); + }); + it('Expects to fail when aTokenAddress is not address', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const aTokenAddress = 'asdf'; + await expect(async () => + weth.withdrawETH({ + lendingPool, + user, + amount, + aTokenAddress, + }), + ).rejects.toThrowError( + `Address: ${aTokenAddress} is not a valid ethereum Address`, + ); + }); + it('Expects to fail when onBehalfOf is not address', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const onBehalfOf = 'asdf'; + await expect(async () => + weth.withdrawETH({ + lendingPool, + user, + amount, + aTokenAddress, + onBehalfOf, + }), + ).rejects.toThrowError( + `Address: ${onBehalfOf} is not a valid ethereum Address`, + ); + }); + }); + describe('borrowETH', () => { + const user = '0x0000000000000000000000000000000000000003'; + const onBehalfOf = '0x0000000000000000000000000000000000000004'; + const interestRateMode = InterestRate.Stable; + const amount = '123.456'; + const provider: providers.Provider = new providers.JsonRpcProvider(); + jest + .spyOn(provider, 'getGasPrice') + .mockImplementation(async () => Promise.resolve(BigNumber.from(1))); + const erc20Service = new ERC20Service(provider); + + afterEach(() => { + jest.clearAllMocks(); + }); + it('Expects the repay tx object to be correct with all params and stable rate mode', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + + const txObj = weth.repayETH({ + lendingPool, + user, + amount, + interestRateMode, + onBehalfOf, + }); + + expect(txObj.length).toEqual(1); + expect(txObj[0].txType).toEqual(eEthereumTxType.DLP_ACTION); + + const tx: transactionType = await txObj[0].tx(); + expect(tx.to).toEqual(wethGatewayAddress); + expect(tx.from).toEqual(user); + expect(tx.gasLimit).toEqual(BigNumber.from(1)); + + const decoded = utils.defaultAbiCoder.decode( + ['address', 'uint256', 'uint256', 'address'], + utils.hexDataSlice(tx.data ?? '', 4), + ); + + expect(decoded[0]).toEqual(lendingPool); + expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); + expect(decoded[2]).toEqual(BigNumber.from(1)); + expect(decoded[3]).toEqual(onBehalfOf); + + // gas price + const gasPrice: GasType | null = await txObj[0].gas(); + expect(gasPrice).not.toBeNull(); + expect(gasPrice?.gasLimit).toEqual('1'); + expect(gasPrice?.gasPrice).toEqual('1'); + }); + it('Expects the repay tx object to be correct with all params and variable rate mode', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const interestRateMode = InterestRate.Variable; + const txObj = weth.repayETH({ + lendingPool, + user, + amount, + interestRateMode, + onBehalfOf, + }); + + expect(txObj.length).toEqual(1); + expect(txObj[0].txType).toEqual(eEthereumTxType.DLP_ACTION); + + const tx: transactionType = await txObj[0].tx(); + expect(tx.to).toEqual(wethGatewayAddress); + expect(tx.from).toEqual(user); + expect(tx.gasLimit).toEqual(BigNumber.from(1)); + + const decoded = utils.defaultAbiCoder.decode( + ['address', 'uint256', 'uint256', 'address'], + utils.hexDataSlice(tx.data ?? '', 4), + ); + + expect(decoded[0]).toEqual(lendingPool); + expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); + expect(decoded[2]).toEqual(BigNumber.from(2)); + expect(decoded[3]).toEqual(onBehalfOf); + + // gas price + const gasPrice: GasType | null = await txObj[0].gas(); + expect(gasPrice).not.toBeNull(); + expect(gasPrice?.gasLimit).toEqual('1'); + expect(gasPrice?.gasPrice).toEqual('1'); + }); + it('Expects the repay tx object to be correct with all params and none rate mode', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const interestRateMode = InterestRate.None; + const txObj = weth.repayETH({ + lendingPool, + user, + amount, + interestRateMode, + onBehalfOf, + }); + + expect(txObj.length).toEqual(1); + expect(txObj[0].txType).toEqual(eEthereumTxType.DLP_ACTION); + + const tx: transactionType = await txObj[0].tx(); + expect(tx.to).toEqual(wethGatewayAddress); + expect(tx.from).toEqual(user); + expect(tx.gasLimit).toEqual(BigNumber.from(1)); + + const decoded = utils.defaultAbiCoder.decode( + ['address', 'uint256', 'uint256', 'address'], + utils.hexDataSlice(tx.data ?? '', 4), + ); + + expect(decoded[0]).toEqual(lendingPool); + expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); + expect(decoded[2]).toEqual(BigNumber.from(1)); + expect(decoded[3]).toEqual(onBehalfOf); + + // gas price + const gasPrice: GasType | null = await txObj[0].gas(); + expect(gasPrice).not.toBeNull(); + expect(gasPrice?.gasLimit).toEqual('1'); + expect(gasPrice?.gasPrice).toEqual('1'); + }); + it('Expects the repay tx object to be correct without onBehalfOf', async () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + + const txObj = weth.repayETH({ + lendingPool, + user, + amount, + interestRateMode, + }); + + expect(txObj.length).toEqual(1); + expect(txObj[0].txType).toEqual(eEthereumTxType.DLP_ACTION); + + const tx: transactionType = await txObj[0].tx(); + expect(tx.to).toEqual(wethGatewayAddress); + expect(tx.from).toEqual(user); + expect(tx.gasLimit).toEqual(BigNumber.from(1)); + + const decoded = utils.defaultAbiCoder.decode( + ['address', 'uint256', 'uint256', 'address'], + utils.hexDataSlice(tx.data ?? '', 4), + ); + + expect(decoded[0]).toEqual(lendingPool); + expect(decoded[1]).toEqual(BigNumber.from(valueToWei(amount, 18))); + expect(decoded[2]).toEqual(BigNumber.from(1)); + expect(decoded[3]).toEqual(user); + + // gas price + const gasPrice: GasType | null = await txObj[0].gas(); + expect(gasPrice).not.toBeNull(); + expect(gasPrice?.gasLimit).toEqual('1'); + expect(gasPrice?.gasPrice).toEqual('1'); + }); + it('Expects to fail when initialized without gateway address', () => { + const weth = new WETHGatewayService(provider, erc20Service); + + const txObj = weth.repayETH({ + lendingPool, + user, + amount, + interestRateMode, + }); + + expect(txObj.length).toEqual(0); + }); + it('Expects to fail when user is not address', () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const user = 'asdf'; + expect(() => + weth.repayETH({ + lendingPool, + user, + amount, + interestRateMode, + }), + ).toThrowError(`Address: ${user} is not a valid ethereum Address`); + }); + it('Expects to fail when lendingPool is not address', () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const lendingPool = 'asdf'; + expect(() => + weth.repayETH({ + lendingPool, + user, + amount, + interestRateMode, + }), + ).toThrowError(`Address: ${lendingPool} is not a valid ethereum Address`); + }); + it('Expects to fail when amount is not positive', () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const amount = '0'; + expect(() => + weth.repayETH({ + lendingPool, + user, + amount, + interestRateMode, + }), + ).toThrowError(`Amount: ${amount} needs to be greater than 0`); + }); + it('Expects to fail when amount is not number', () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const amount = 'asdf'; + expect(() => + weth.repayETH({ + lendingPool, + user, + amount, + interestRateMode, + }), + ).toThrowError(`Amount: ${amount} needs to be greater than 0`); + }); + it('Expects to fail when onBehalfOf is not address', () => { + const weth = new WETHGatewayService( + provider, + erc20Service, + wethGatewayAddress, + ); + const onBehalfOf = 'asdf'; + expect(() => + weth.repayETH({ + lendingPool, + user, + amount, + interestRateMode, + onBehalfOf, + }), + ).toThrowError(`Address: ${onBehalfOf} is not a valid ethereum Address`); + }); + }); +});