From c872e45e4e34a449720ef28c167cdb0dea78e94c Mon Sep 17 00:00:00 2001 From: defispartan Date: Fri, 30 May 2025 01:41:28 -0400 Subject: [PATCH 1/8] feat: atomic batch wallet capability --- src/hooks/useGetWalletCapabilities.ts | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/hooks/useGetWalletCapabilities.ts diff --git a/src/hooks/useGetWalletCapabilities.ts b/src/hooks/useGetWalletCapabilities.ts new file mode 100644 index 0000000000..9b3f8f33d8 --- /dev/null +++ b/src/hooks/useGetWalletCapabilities.ts @@ -0,0 +1,31 @@ +import { useQuery } from '@tanstack/react-query'; +import { useAccount } from 'wagmi'; +import { useWalletClient } from 'wagmi'; + +export type WalletCapabilities = { + [chainId: number]: boolean; +}; + +export const useGetWalletCapabilities = () => { + const { address } = useAccount(); + const { data: walletClient } = useWalletClient(); + + return useQuery({ + queryFn: async () => { + if (!walletClient || !address) return null; + + const capabilities = await walletClient.getCapabilities(); + + // Transform capabilities into a simple boolean map + const simplifiedCapabilities: WalletCapabilities = {}; + Object.entries(capabilities).forEach(([chainId, chainCapabilities]) => { + const atomicStatus = (chainCapabilities as any)?.atomic?.status; + simplifiedCapabilities[Number(chainId)] = atomicStatus === 'supported' || atomicStatus === 'ready'; + }); + + return simplifiedCapabilities; + }, + queryKey: ['walletCapabilities', address], + enabled: !!walletClient && !!address, + }); +}; \ No newline at end of file From 225db04f892247a906c1c89d51a79af30f104521 Mon Sep 17 00:00:00 2001 From: defispartan Date: Fri, 30 May 2025 01:48:29 -0400 Subject: [PATCH 2/8] feat: wallet capability state --- src/libs/web3-data-provider/Web3Provider.tsx | 37 +++++++++++++------- src/store/walletSlice.ts | 9 ++++- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/libs/web3-data-provider/Web3Provider.tsx b/src/libs/web3-data-provider/Web3Provider.tsx index 26fe83287c..b518a75640 100644 --- a/src/libs/web3-data-provider/Web3Provider.tsx +++ b/src/libs/web3-data-provider/Web3Provider.tsx @@ -13,6 +13,7 @@ import { useShallow } from 'zustand/shallow'; import { Web3Context } from '../hooks/useWeb3Context'; import { getEthersProvider } from './adapters/EthersAdapter'; +import { useGetWalletCapabilities } from 'src/hooks/useGetWalletCapabilities'; export type ERC20TokenType = { address: string; @@ -49,8 +50,12 @@ export const Web3ContextProvider: React.FC<{ children: ReactElement }> = ({ chil const [readOnlyModeAddress, setReadOnlyModeAddress] = useState(); const [switchNetworkError, setSwitchNetworkError] = useState(); - const [setAccount, setConnectedAccountIsContract] = useRootStore( - useShallow((store) => [store.setAccount, store.setConnectedAccountIsContract]) + const [setAccount, setConnectedAccountIsContract, setWalletCapabilities] = useRootStore( + useShallow((store) => [ + store.setAccount, + store.setConnectedAccountIsContract, + store.setWalletCapabilities, + ]) ); const account = address; @@ -61,6 +66,23 @@ export const Web3ContextProvider: React.FC<{ children: ReactElement }> = ({ chil } const { data: isContractAddress } = useIsContractAddress(account || '', chainId); + const { data: walletCapabilities } = useGetWalletCapabilities(); + + useEffect(() => { + if (!account) { + setConnectedAccountIsContract(false); + setWalletCapabilities(null); + return; + } + + if (isContractAddress) { + setConnectedAccountIsContract(true); + } + + if (walletCapabilities) { + setWalletCapabilities(walletCapabilities); + } + }, [isContractAddress, setConnectedAccountIsContract, account, walletCapabilities, setWalletCapabilities]); useEffect(() => { if (didInit) { @@ -185,17 +207,6 @@ export const Web3ContextProvider: React.FC<{ children: ReactElement }> = ({ chil } }, [readOnlyModeAddress, setAccount]); - useEffect(() => { - if (!account) { - setConnectedAccountIsContract(false); - return; - } - - if (isContractAddress) { - setConnectedAccountIsContract(true); - } - }, [isContractAddress, setConnectedAccountIsContract, account]); - return ( void; connectedAccountIsContract: boolean; setConnectedAccountIsContract: (isContract: boolean) => void; -} + walletCapabilities: WalletCapabilities | null; + setWalletCapabilities: (capabilities: WalletCapabilities | null) => void; +}; const getWalletPreferences = () => { const walletPreference = localStorage.getItem('walletApprovalPreferences'); @@ -79,4 +82,8 @@ export const createWalletSlice: StateCreator< setConnectedAccountIsContract(isContract) { set({ connectedAccountIsContract: isContract }); }, + walletCapabilities: null, + setWalletCapabilities(capabilities) { + set({ walletCapabilities: capabilities }); + }, }); From 94f59cd70c6b94db1a1cc91485cb4e5eeccf3071 Mon Sep 17 00:00:00 2001 From: defispartan Date: Tue, 10 Jun 2025 02:31:51 -0400 Subject: [PATCH 3/8] feat: batch transaction wallet capatbility state --- src/hooks/useGetWalletCapabilities.ts | 15 ++++++--------- src/libs/web3-data-provider/Web3Provider.tsx | 10 ++++++++-- src/store/walletSlice.ts | 5 +++-- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/hooks/useGetWalletCapabilities.ts b/src/hooks/useGetWalletCapabilities.ts index 9b3f8f33d8..2cf43dfe4a 100644 --- a/src/hooks/useGetWalletCapabilities.ts +++ b/src/hooks/useGetWalletCapabilities.ts @@ -1,6 +1,5 @@ import { useQuery } from '@tanstack/react-query'; -import { useAccount } from 'wagmi'; -import { useWalletClient } from 'wagmi'; +import { useAccount, useWalletClient } from 'wagmi'; export type WalletCapabilities = { [chainId: number]: boolean; @@ -13,19 +12,17 @@ export const useGetWalletCapabilities = () => { return useQuery({ queryFn: async () => { if (!walletClient || !address) return null; - const capabilities = await walletClient.getCapabilities(); - - // Transform capabilities into a simple boolean map const simplifiedCapabilities: WalletCapabilities = {}; Object.entries(capabilities).forEach(([chainId, chainCapabilities]) => { - const atomicStatus = (chainCapabilities as any)?.atomic?.status; - simplifiedCapabilities[Number(chainId)] = atomicStatus === 'supported' || atomicStatus === 'ready'; + const atomicStatus = (chainCapabilities as { atomic?: { status?: string } })?.atomic + ?.status; + simplifiedCapabilities[Number(chainId)] = + atomicStatus === 'supported' || atomicStatus === 'ready'; }); - return simplifiedCapabilities; }, queryKey: ['walletCapabilities', address], enabled: !!walletClient && !!address, }); -}; \ No newline at end of file +}; diff --git a/src/libs/web3-data-provider/Web3Provider.tsx b/src/libs/web3-data-provider/Web3Provider.tsx index b518a75640..f3c40f7303 100644 --- a/src/libs/web3-data-provider/Web3Provider.tsx +++ b/src/libs/web3-data-provider/Web3Provider.tsx @@ -3,6 +3,7 @@ import { SignatureLike } from '@ethersproject/bytes'; import { JsonRpcProvider, TransactionResponse } from '@ethersproject/providers'; import { BigNumber, PopulatedTransaction, utils } from 'ethers'; import React, { ReactElement, useEffect, useState } from 'react'; +import { useGetWalletCapabilities } from 'src/hooks/useGetWalletCapabilities'; import { useIsContractAddress } from 'src/hooks/useIsContractAddress'; import { useRootStore } from 'src/store/root'; import { wagmiConfig } from 'src/ui-config/wagmiConfig'; @@ -13,7 +14,6 @@ import { useShallow } from 'zustand/shallow'; import { Web3Context } from '../hooks/useWeb3Context'; import { getEthersProvider } from './adapters/EthersAdapter'; -import { useGetWalletCapabilities } from 'src/hooks/useGetWalletCapabilities'; export type ERC20TokenType = { address: string; @@ -82,7 +82,13 @@ export const Web3ContextProvider: React.FC<{ children: ReactElement }> = ({ chil if (walletCapabilities) { setWalletCapabilities(walletCapabilities); } - }, [isContractAddress, setConnectedAccountIsContract, account, walletCapabilities, setWalletCapabilities]); + }, [ + isContractAddress, + setConnectedAccountIsContract, + account, + walletCapabilities, + setWalletCapabilities, + ]); useEffect(() => { if (didInit) { diff --git a/src/store/walletSlice.ts b/src/store/walletSlice.ts index 3c0470b69f..a32c49fdec 100644 --- a/src/store/walletSlice.ts +++ b/src/store/walletSlice.ts @@ -1,11 +1,12 @@ -import { StateCreator } from 'zustand'; import { WalletCapabilities } from 'src/hooks/useGetWalletCapabilities'; +import { StateCreator } from 'zustand'; import { RootStore } from './root'; export enum ApprovalMethod { APPROVE = 'Transaction', PERMIT = 'Signed message', + BATCH = 'Batch transaction', } export interface WalletSlice { @@ -22,7 +23,7 @@ export interface WalletSlice { setConnectedAccountIsContract: (isContract: boolean) => void; walletCapabilities: WalletCapabilities | null; setWalletCapabilities: (capabilities: WalletCapabilities | null) => void; -}; +} const getWalletPreferences = () => { const walletPreference = localStorage.getItem('walletApprovalPreferences'); From e79c9280735ee6dd6f33bbc1ee15ebc916861e17 Mon Sep 17 00:00:00 2001 From: defispartan Date: Tue, 10 Jun 2025 02:32:48 -0400 Subject: [PATCH 4/8] feat: batch transaciton method toggle --- .../ApprovalMethodToggleButton.tsx | 118 ++++++++++++------ .../FlowCommons/RightHelperText.tsx | 10 +- .../transactions/TxActionsWrapper.tsx | 10 +- 3 files changed, 93 insertions(+), 45 deletions(-) diff --git a/src/components/transactions/FlowCommons/ApprovalMethodToggleButton.tsx b/src/components/transactions/FlowCommons/ApprovalMethodToggleButton.tsx index 0767b1cdfc..151d76cdb6 100644 --- a/src/components/transactions/FlowCommons/ApprovalMethodToggleButton.tsx +++ b/src/components/transactions/FlowCommons/ApprovalMethodToggleButton.tsx @@ -1,5 +1,4 @@ -import { CheckIcon } from '@heroicons/react/outline'; -import { CogIcon } from '@heroicons/react/solid'; +import { SwitchHorizontalIcon } from '@heroicons/react/outline'; import { Trans } from '@lingui/macro'; import { Box, @@ -10,19 +9,21 @@ import { SvgIcon, Typography, } from '@mui/material'; -import * as React from 'react'; +import { useState } from 'react'; import { ApprovalMethod } from 'src/store/walletSlice'; interface ApprovalMethodToggleButtonProps { currentMethod: ApprovalMethod; - setMethod: (newMethod: ApprovalMethod) => void; + setMethod: (method: ApprovalMethod) => void; + showBatchOption?: boolean; } export const ApprovalMethodToggleButton = ({ currentMethod, setMethod, + showBatchOption = false, }: ApprovalMethodToggleButtonProps) => { - const [anchorEl, setAnchorEl] = React.useState(null); + const [anchorEl, setAnchorEl] = useState(null); const open = Boolean(anchorEl); const handleClick = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); @@ -34,64 +35,99 @@ export const ApprovalMethodToggleButton = ({ return ( <> - - {currentMethod} + + {currentMethod === ApprovalMethod.BATCH && Batch Transaction} + {currentMethod === ApprovalMethod.PERMIT && Signature} + {currentMethod === ApprovalMethod.APPROVE && Transaction} - - + + - + {showBatchOption && ( + setMethod(ApprovalMethod.BATCH)} + selected={currentMethod === ApprovalMethod.BATCH} + > + + + + + + + Batch Transaction + + + )} setMethod(ApprovalMethod.PERMIT)} selected={currentMethod === ApprovalMethod.PERMIT} - value={ApprovalMethod.PERMIT} - onClick={() => { - if (currentMethod === ApprovalMethod.APPROVE) { - setMethod(ApprovalMethod.PERMIT); - } - handleClose(); - }} > - - {ApprovalMethod.PERMIT} - - {currentMethod === ApprovalMethod.PERMIT && } + + + + + Signature + - setMethod(ApprovalMethod.APPROVE)} selected={currentMethod === ApprovalMethod.APPROVE} - value={ApprovalMethod.APPROVE} - onClick={() => { - if (currentMethod === ApprovalMethod.PERMIT) { - setMethod(ApprovalMethod.APPROVE); - } - handleClose(); - }} > - - {ApprovalMethod.APPROVE} - - {currentMethod === ApprovalMethod.APPROVE && } + + + + + Transaction + diff --git a/src/components/transactions/FlowCommons/RightHelperText.tsx b/src/components/transactions/FlowCommons/RightHelperText.tsx index e401d04e65..64873b448b 100644 --- a/src/components/transactions/FlowCommons/RightHelperText.tsx +++ b/src/components/transactions/FlowCommons/RightHelperText.tsx @@ -12,6 +12,7 @@ import { useShallow } from 'zustand/shallow'; export type RightHelperTextProps = { approvalHash?: string; tryPermit?: boolean; + showBatchOption?: boolean; }; const ExtLinkIcon = () => ( @@ -20,7 +21,11 @@ const ExtLinkIcon = () => ( ); -export const RightHelperText = ({ approvalHash, tryPermit }: RightHelperTextProps) => { +export const RightHelperText = ({ + approvalHash, + tryPermit, + showBatchOption = false, +}: RightHelperTextProps) => { const [ account, walletApprovalMethodPreference, @@ -46,7 +51,7 @@ export const RightHelperText = ({ approvalHash, tryPermit }: RightHelperTextProp if (isContractAddress && walletApprovalMethodPreference === ApprovalMethod.PERMIT) { setWalletApprovalMethodPreference(ApprovalMethod.APPROVE); } - }, [isContractAddress]); + }, [isContractAddress, walletApprovalMethodPreference, setWalletApprovalMethodPreference]); // a signature is not submitted on-chain so there is no link to review if (!approvalHash && !isSigned && tryPermit) @@ -58,6 +63,7 @@ export const RightHelperText = ({ approvalHash, tryPermit }: RightHelperTextProp setWalletApprovalMethodPreference(method)} + showBatchOption={showBatchOption} /> ); diff --git a/src/components/transactions/TxActionsWrapper.tsx b/src/components/transactions/TxActionsWrapper.tsx index af16ded1bd..e77b50824b 100644 --- a/src/components/transactions/TxActionsWrapper.tsx +++ b/src/components/transactions/TxActionsWrapper.tsx @@ -33,6 +33,7 @@ interface TxActionsWrapperProps extends BoxProps { }; tryPermit?: boolean; event?: TrackEventProps; + showBatchOption?: boolean; } export const TxActionsWrapper = ({ @@ -54,6 +55,7 @@ export const TxActionsWrapper = ({ errorParams, tryPermit, event, + showBatchOption = false, ...rest }: TxActionsWrapperProps) => { const { txError } = useModalContext(); @@ -127,9 +129,13 @@ export const TxActionsWrapper = ({ const approvalParams = getApprovalParams(); return ( - {approvalParams && !readOnlyModeAddress && ( + {(approvalParams || showBatchOption) && !readOnlyModeAddress && ( - + )} From 181c8551a4ae2caa209128dfb28c573b1f6e79b7 Mon Sep 17 00:00:00 2001 From: defispartan Date: Tue, 10 Jun 2025 02:33:48 -0400 Subject: [PATCH 5/8] feat: BatchSupply action --- .../Supply/BatchSupplyActions.tsx | 261 +++++ src/libs/abis/pool_abi.ts | 893 ++++++++++++++++++ 2 files changed, 1154 insertions(+) create mode 100644 src/components/transactions/Supply/BatchSupplyActions.tsx create mode 100644 src/libs/abis/pool_abi.ts diff --git a/src/components/transactions/Supply/BatchSupplyActions.tsx b/src/components/transactions/Supply/BatchSupplyActions.tsx new file mode 100644 index 0000000000..e031fe5004 --- /dev/null +++ b/src/components/transactions/Supply/BatchSupplyActions.tsx @@ -0,0 +1,261 @@ +import { gasLimitRecommendations, ProtocolAction } from '@aave/contract-helpers'; +import { TransactionResponse } from '@ethersproject/providers'; +import { Trans } from '@lingui/macro'; +import { BoxProps } from '@mui/material'; +import { useQueryClient } from '@tanstack/react-query'; +import React, { useEffect, useState } from 'react'; +import { SignedParams, useApprovalTx } from 'src/hooks/useApprovalTx'; +import { usePoolApprovedAmount } from 'src/hooks/useApprovedAmount'; +import { useModalContext } from 'src/hooks/useModal'; +import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; +import { useRootStore } from 'src/store/root'; +import { ApprovalMethod } from 'src/store/walletSlice'; +import { getErrorTextFromError, TxAction } from 'src/ui-config/errorMapping'; +import { queryKeysFactory } from 'src/ui-config/queries'; +import { encodeFunctionData, erc20Abi, parseUnits } from 'viem'; +import { useAccount, useSendCalls } from 'wagmi'; +import { useShallow } from 'zustand/shallow'; + +import { poolAbi } from '../../../libs/abis/pool_abi'; +import { TxActionsWrapper } from '../TxActionsWrapper'; +import { APPROVAL_GAS_LIMIT, checkRequiresApproval } from '../utils'; + +export interface BatchSupplyActionsProps extends BoxProps { + amountToSupply: string; + isWrongNetwork: boolean; + poolAddress: string; + symbol: string; + blocked: boolean; + decimals: number; + isWrappedBaseAsset: boolean; +} + +export function BatchSupplyActions({ + amountToSupply, + isWrongNetwork, + poolAddress, + symbol, + blocked, + decimals, + isWrappedBaseAsset, + ...props +}: BatchSupplyActionsProps) { + const { address } = useAccount(); + const queryClient = useQueryClient(); + const { + approvalTxState, + mainTxState, + loadingTxns, + setLoadingTxns, + setApprovalTxState, + setMainTxState, + setGasLimit, + setTxError, + } = useModalContext(); + const { sendTx } = useWeb3Context(); + const [signatureParams, setSignatureParams] = useState(); + + const [ + tryPermit, + supply, + supplyWithPermit, + walletApprovalMethodPreference, + estimateGasLimit, + addTransaction, + currentMarketData, + ] = useRootStore( + useShallow((state) => [ + state.tryPermit, + state.supply, + state.supplyWithPermit, + state.walletApprovalMethodPreference, + state.estimateGasLimit, + state.addTransaction, + state.currentMarketData, + ]) + ); + + const { + data: approvedAmount, + refetch: fetchApprovedAmount, + isRefetching: fetchingApprovedAmount, + } = usePoolApprovedAmount(currentMarketData, poolAddress); + + setLoadingTxns(fetchingApprovedAmount); + + const requiresApproval = + Number(amountToSupply) !== 0 && + checkRequiresApproval({ + approvedAmount: approvedAmount?.amount || '0', + amount: amountToSupply, + signedAmount: signatureParams ? signatureParams.amount : '0', + }); + + if (requiresApproval && approvalTxState?.success) { + // There was a successful approval tx, but the approval amount is not enough. + // Clear the state to prompt for another approval. + setApprovalTxState({}); + } + + const permitAvailable = tryPermit({ reserveAddress: poolAddress, isWrappedBaseAsset }); + const usePermit = permitAvailable && walletApprovalMethodPreference === ApprovalMethod.PERMIT; + + const { approval } = useApprovalTx({ + usePermit, + approvedAmount, + requiresApproval, + assetAddress: poolAddress, + symbol, + decimals, + signatureAmount: amountToSupply, + onApprovalTxConfirmed: fetchApprovedAmount, + onSignTxCompleted: (signedParams) => setSignatureParams(signedParams), + }); + + const { sendCalls, isPending } = useSendCalls({ + mutation: { + onSuccess: (hash) => { + console.log('BATCH HASH', hash); + queryClient.invalidateQueries({ queryKey: queryKeysFactory.pool }); + setMainTxState({ success: true, txHash: hash.id }); + }, + onError: (error) => { + console.log('ERROR', error); + const parsedError = getErrorTextFromError(error, TxAction.GAS_ESTIMATION, false); + setTxError(parsedError); + }, + }, + }); + + // Update gas estimation + useEffect(() => { + let supplyGasLimit = 0; + if (walletApprovalMethodPreference === ApprovalMethod.BATCH) { + supplyGasLimit = Number(gasLimitRecommendations[ProtocolAction.supply].recommended); + if (requiresApproval) { + supplyGasLimit += Number(APPROVAL_GAS_LIMIT); + } + } else if (usePermit) { + supplyGasLimit = Number(gasLimitRecommendations[ProtocolAction.supplyWithPermit].recommended); + } else { + supplyGasLimit = Number(gasLimitRecommendations[ProtocolAction.supply].recommended); + if (requiresApproval && !approvalTxState.success) { + supplyGasLimit += Number(APPROVAL_GAS_LIMIT); + } + } + setGasLimit(supplyGasLimit.toString()); + }, [requiresApproval, approvalTxState, usePermit, walletApprovalMethodPreference, setGasLimit]); + + const handleSupply = async () => { + try { + setMainTxState({ ...mainTxState, loading: true }); + + if (walletApprovalMethodPreference === ApprovalMethod.BATCH) { + const calls = []; + + if (requiresApproval) { + const approveData = encodeFunctionData({ + abi: erc20Abi, + functionName: 'approve', + args: [poolAddress as `0x${string}`, parseUnits(amountToSupply, decimals)], + }); + calls.push({ + to: poolAddress, + data: approveData, + }); + } + + const supplyData = encodeFunctionData({ + abi: poolAbi, + functionName: 'supply', + args: [ + poolAddress as `0x${string}`, + parseUnits(amountToSupply, decimals), + address || '0x0', + 0, + ], + }); + calls.push({ + to: poolAddress, + data: supplyData, + }); + + sendCalls({ + calls, + }); + } else { + let response: TransactionResponse; + let action = ProtocolAction.default; + + if (usePermit && signatureParams) { + action = ProtocolAction.supplyWithPermit; + let signedSupplyWithPermitTxData = supplyWithPermit({ + signature: signatureParams.signature, + amount: parseUnits(amountToSupply, decimals).toString(), + reserve: poolAddress, + deadline: signatureParams.deadline, + }); + + signedSupplyWithPermitTxData = await estimateGasLimit(signedSupplyWithPermitTxData); + response = await sendTx(signedSupplyWithPermitTxData); + + await response.wait(1); + } else { + action = ProtocolAction.supply; + let supplyTxData = supply({ + amount: parseUnits(amountToSupply, decimals).toString(), + reserve: poolAddress, + }); + supplyTxData = await estimateGasLimit(supplyTxData); + response = await sendTx(supplyTxData); + + await response.wait(1); + } + + setMainTxState({ + txHash: response.hash, + loading: false, + success: true, + }); + + addTransaction(response.hash, { + action, + txState: 'success', + asset: poolAddress, + amount: amountToSupply, + assetName: symbol, + }); + + queryClient.invalidateQueries({ queryKey: queryKeysFactory.pool }); + } + } catch (error) { + const parsedError = getErrorTextFromError(error, TxAction.GAS_ESTIMATION, false); + setTxError(parsedError); + setMainTxState({ + txHash: undefined, + loading: false, + }); + } + }; + + return ( + Supply {symbol}} + actionInProgressText={Supplying {symbol}} + handleApproval={approval} + handleAction={handleSupply} + requiresApproval={requiresApproval && walletApprovalMethodPreference !== ApprovalMethod.BATCH} + tryPermit={permitAvailable} + showBatchOption={true} + sx={props.sx} + /> + ); +} diff --git a/src/libs/abis/pool_abi.ts b/src/libs/abis/pool_abi.ts new file mode 100644 index 0000000000..c6ddcbf190 --- /dev/null +++ b/src/libs/abis/pool_abi.ts @@ -0,0 +1,893 @@ +export const poolAbi = [ + { + inputs: [ + { internalType: 'contract IPoolAddressesProvider', name: 'provider', type: 'address' }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'reserve', type: 'address' }, + { indexed: true, internalType: 'address', name: 'backer', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'amount', type: 'uint256' }, + { indexed: false, internalType: 'uint256', name: 'fee', type: 'uint256' }, + ], + name: 'BackUnbacked', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'reserve', type: 'address' }, + { indexed: false, internalType: 'address', name: 'user', type: 'address' }, + { indexed: true, internalType: 'address', name: 'onBehalfOf', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'amount', type: 'uint256' }, + { + indexed: false, + internalType: 'enum DataTypes.InterestRateMode', + name: 'interestRateMode', + type: 'uint8', + }, + { indexed: false, internalType: 'uint256', name: 'borrowRate', type: 'uint256' }, + { indexed: true, internalType: 'uint16', name: 'referralCode', type: 'uint16' }, + ], + name: 'Borrow', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'reserve', type: 'address' }, + { indexed: false, internalType: 'address', name: 'caller', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'amountCovered', type: 'uint256' }, + ], + name: 'DeficitCovered', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'user', type: 'address' }, + { indexed: true, internalType: 'address', name: 'debtAsset', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'amountCreated', type: 'uint256' }, + ], + name: 'DeficitCreated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'target', type: 'address' }, + { indexed: false, internalType: 'address', name: 'initiator', type: 'address' }, + { indexed: true, internalType: 'address', name: 'asset', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'amount', type: 'uint256' }, + { + indexed: false, + internalType: 'enum DataTypes.InterestRateMode', + name: 'interestRateMode', + type: 'uint8', + }, + { indexed: false, internalType: 'uint256', name: 'premium', type: 'uint256' }, + { indexed: true, internalType: 'uint16', name: 'referralCode', type: 'uint16' }, + ], + name: 'FlashLoan', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'asset', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'totalDebt', type: 'uint256' }, + ], + name: 'IsolationModeTotalDebtUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'collateralAsset', type: 'address' }, + { indexed: true, internalType: 'address', name: 'debtAsset', type: 'address' }, + { indexed: true, internalType: 'address', name: 'user', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'debtToCover', type: 'uint256' }, + { + indexed: false, + internalType: 'uint256', + name: 'liquidatedCollateralAmount', + type: 'uint256', + }, + { indexed: false, internalType: 'address', name: 'liquidator', type: 'address' }, + { indexed: false, internalType: 'bool', name: 'receiveAToken', type: 'bool' }, + ], + name: 'LiquidationCall', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'reserve', type: 'address' }, + { indexed: false, internalType: 'address', name: 'user', type: 'address' }, + { indexed: true, internalType: 'address', name: 'onBehalfOf', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'amount', type: 'uint256' }, + { indexed: true, internalType: 'uint16', name: 'referralCode', type: 'uint16' }, + ], + name: 'MintUnbacked', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'reserve', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'amountMinted', type: 'uint256' }, + ], + name: 'MintedToTreasury', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'reserve', type: 'address' }, + { indexed: true, internalType: 'address', name: 'user', type: 'address' }, + { indexed: true, internalType: 'address', name: 'repayer', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'amount', type: 'uint256' }, + { indexed: false, internalType: 'bool', name: 'useATokens', type: 'bool' }, + ], + name: 'Repay', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'reserve', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'liquidityRate', type: 'uint256' }, + { indexed: false, internalType: 'uint256', name: 'stableBorrowRate', type: 'uint256' }, + { indexed: false, internalType: 'uint256', name: 'variableBorrowRate', type: 'uint256' }, + { indexed: false, internalType: 'uint256', name: 'liquidityIndex', type: 'uint256' }, + { indexed: false, internalType: 'uint256', name: 'variableBorrowIndex', type: 'uint256' }, + ], + name: 'ReserveDataUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'reserve', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'liquidityRate', type: 'uint256' }, + { indexed: false, internalType: 'uint256', name: 'stableBorrowRate', type: 'uint256' }, + { indexed: false, internalType: 'uint256', name: 'variableBorrowRate', type: 'uint256' }, + { indexed: false, internalType: 'uint256', name: 'liquidityIndex', type: 'uint256' }, + { indexed: false, internalType: 'uint256', name: 'variableBorrowIndex', type: 'uint256' }, + ], + name: 'ReserveDataUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'reserve', type: 'address' }, + { indexed: true, internalType: 'address', name: 'user', type: 'address' }, + ], + name: 'ReserveUsedAsCollateralDisabled', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'reserve', type: 'address' }, + { indexed: true, internalType: 'address', name: 'user', type: 'address' }, + ], + name: 'ReserveUsedAsCollateralEnabled', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'reserve', type: 'address' }, + { indexed: false, internalType: 'address', name: 'user', type: 'address' }, + { indexed: true, internalType: 'address', name: 'onBehalfOf', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'amount', type: 'uint256' }, + { indexed: true, internalType: 'uint16', name: 'referralCode', type: 'uint16' }, + ], + name: 'Supply', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'user', type: 'address' }, + { indexed: false, internalType: 'uint8', name: 'categoryId', type: 'uint8' }, + ], + name: 'UserEModeSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'reserve', type: 'address' }, + { indexed: true, internalType: 'address', name: 'user', type: 'address' }, + { indexed: true, internalType: 'address', name: 'to', type: 'address' }, + { indexed: false, internalType: 'uint256', name: 'amount', type: 'uint256' }, + ], + name: 'Withdraw', + type: 'event', + }, + { + inputs: [], + name: 'ADDRESSES_PROVIDER', + outputs: [{ internalType: 'contract IPoolAddressesProvider', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'BRIDGE_PROTOCOL_FEE', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'FLASHLOAN_PREMIUM_TOTAL', + outputs: [{ internalType: 'uint128', name: '', type: 'uint128' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'FLASHLOAN_PREMIUM_TO_PROTOCOL', + outputs: [{ internalType: 'uint128', name: '', type: 'uint128' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'MAX_NUMBER_RESERVES', + outputs: [{ internalType: 'uint16', name: '', type: 'uint16' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'POOL_REVISION', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'UMBRELLA', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'asset', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'uint256', name: 'fee', type: 'uint256' }, + ], + name: 'backUnbacked', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'asset', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'uint256', name: 'interestRateMode', type: 'uint256' }, + { internalType: 'uint16', name: 'referralCode', type: 'uint16' }, + { internalType: 'address', name: 'onBehalfOf', type: 'address' }, + ], + name: 'borrow', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint8', name: 'id', type: 'uint8' }, + { + components: [ + { internalType: 'uint16', name: 'ltv', type: 'uint16' }, + { internalType: 'uint16', name: 'liquidationThreshold', type: 'uint16' }, + { internalType: 'uint16', name: 'liquidationBonus', type: 'uint16' }, + { internalType: 'string', name: 'label', type: 'string' }, + ], + internalType: 'struct DataTypes.EModeCategoryBaseConfiguration', + name: 'category', + type: 'tuple', + }, + ], + name: 'configureEModeCategory', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint8', name: 'id', type: 'uint8' }, + { internalType: 'uint128', name: 'borrowableBitmap', type: 'uint128' }, + ], + name: 'configureEModeCategoryBorrowableBitmap', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint8', name: 'id', type: 'uint8' }, + { internalType: 'uint128', name: 'collateralBitmap', type: 'uint128' }, + ], + name: 'configureEModeCategoryCollateralBitmap', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'asset', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'address', name: 'onBehalfOf', type: 'address' }, + { internalType: 'uint16', name: 'referralCode', type: 'uint16' }, + ], + name: 'deposit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'asset', type: 'address' }], + name: 'dropReserve', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'asset', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + ], + name: 'eliminateReserveDeficit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'asset', type: 'address' }, + { internalType: 'address', name: 'from', type: 'address' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'uint256', name: 'balanceFromBefore', type: 'uint256' }, + { internalType: 'uint256', name: 'balanceToBefore', type: 'uint256' }, + ], + name: 'finalizeTransfer', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'receiverAddress', type: 'address' }, + { internalType: 'address[]', name: 'assets', type: 'address[]' }, + { internalType: 'uint256[]', name: 'amounts', type: 'uint256[]' }, + { internalType: 'uint256[]', name: 'interestRateModes', type: 'uint256[]' }, + { internalType: 'address', name: 'onBehalfOf', type: 'address' }, + { internalType: 'bytes', name: 'params', type: 'bytes' }, + { internalType: 'uint16', name: 'referralCode', type: 'uint16' }, + ], + name: 'flashLoan', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'receiverAddress', type: 'address' }, + { internalType: 'address', name: 'asset', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'bytes', name: 'params', type: 'bytes' }, + { internalType: 'uint16', name: 'referralCode', type: 'uint16' }, + ], + name: 'flashLoanSimple', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'getBorrowLogic', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [], + name: 'getBridgeLogic', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'asset', type: 'address' }], + name: 'getConfiguration', + outputs: [ + { + components: [{ internalType: 'uint256', name: 'data', type: 'uint256' }], + internalType: 'struct DataTypes.ReserveConfigurationMap', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'uint8', name: 'id', type: 'uint8' }], + name: 'getEModeCategoryBorrowableBitmap', + outputs: [{ internalType: 'uint128', name: '', type: 'uint128' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'uint8', name: 'id', type: 'uint8' }], + name: 'getEModeCategoryCollateralBitmap', + outputs: [{ internalType: 'uint128', name: '', type: 'uint128' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'uint8', name: 'id', type: 'uint8' }], + name: 'getEModeCategoryCollateralConfig', + outputs: [ + { + components: [ + { internalType: 'uint16', name: 'ltv', type: 'uint16' }, + { internalType: 'uint16', name: 'liquidationThreshold', type: 'uint16' }, + { internalType: 'uint16', name: 'liquidationBonus', type: 'uint16' }, + ], + internalType: 'struct DataTypes.CollateralConfig', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'uint8', name: 'id', type: 'uint8' }], + name: 'getEModeCategoryData', + outputs: [ + { + components: [ + { internalType: 'uint16', name: 'ltv', type: 'uint16' }, + { internalType: 'uint16', name: 'liquidationThreshold', type: 'uint16' }, + { internalType: 'uint16', name: 'liquidationBonus', type: 'uint16' }, + { internalType: 'address', name: 'priceSource', type: 'address' }, + { internalType: 'string', name: 'label', type: 'string' }, + ], + internalType: 'struct DataTypes.EModeCategoryLegacy', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'uint8', name: 'id', type: 'uint8' }], + name: 'getEModeCategoryLabel', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getEModeLogic', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [], + name: 'getFlashLoanLogic', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'asset', type: 'address' }], + name: 'getLiquidationGracePeriod', + outputs: [{ internalType: 'uint40', name: '', type: 'uint40' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getLiquidationLogic', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [], + name: 'getPoolLogic', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'asset', type: 'address' }], + name: 'getReserveAToken', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'uint16', name: 'id', type: 'uint16' }], + name: 'getReserveAddressById', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'asset', type: 'address' }], + name: 'getReserveData', + outputs: [ + { + components: [ + { + components: [{ internalType: 'uint256', name: 'data', type: 'uint256' }], + internalType: 'struct DataTypes.ReserveConfigurationMap', + name: 'configuration', + type: 'tuple', + }, + { internalType: 'uint128', name: 'liquidityIndex', type: 'uint128' }, + { internalType: 'uint128', name: 'currentLiquidityRate', type: 'uint128' }, + { internalType: 'uint128', name: 'variableBorrowIndex', type: 'uint128' }, + { internalType: 'uint128', name: 'currentVariableBorrowRate', type: 'uint128' }, + { internalType: 'uint128', name: 'currentStableBorrowRate', type: 'uint128' }, + { internalType: 'uint40', name: 'lastUpdateTimestamp', type: 'uint40' }, + { internalType: 'uint16', name: 'id', type: 'uint16' }, + { internalType: 'address', name: 'aTokenAddress', type: 'address' }, + { internalType: 'address', name: 'stableDebtTokenAddress', type: 'address' }, + { internalType: 'address', name: 'variableDebtTokenAddress', type: 'address' }, + { internalType: 'address', name: 'interestRateStrategyAddress', type: 'address' }, + { internalType: 'uint128', name: 'accruedToTreasury', type: 'uint128' }, + { internalType: 'uint128', name: 'unbacked', type: 'uint128' }, + { internalType: 'uint128', name: 'isolationModeTotalDebt', type: 'uint128' }, + ], + internalType: 'struct DataTypes.ReserveDataLegacy', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'asset', type: 'address' }], + name: 'getReserveDeficit', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'asset', type: 'address' }], + name: 'getReserveNormalizedIncome', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'asset', type: 'address' }], + name: 'getReserveNormalizedVariableDebt', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'asset', type: 'address' }], + name: 'getReserveVariableDebtToken', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getReservesCount', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getReservesList', + outputs: [{ internalType: 'address[]', name: '', type: 'address[]' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getSupplyLogic', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'user', type: 'address' }], + name: 'getUserAccountData', + outputs: [ + { internalType: 'uint256', name: 'totalCollateralBase', type: 'uint256' }, + { internalType: 'uint256', name: 'totalDebtBase', type: 'uint256' }, + { internalType: 'uint256', name: 'availableBorrowsBase', type: 'uint256' }, + { internalType: 'uint256', name: 'currentLiquidationThreshold', type: 'uint256' }, + { internalType: 'uint256', name: 'ltv', type: 'uint256' }, + { internalType: 'uint256', name: 'healthFactor', type: 'uint256' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'user', type: 'address' }], + name: 'getUserConfiguration', + outputs: [ + { + components: [{ internalType: 'uint256', name: 'data', type: 'uint256' }], + internalType: 'struct DataTypes.UserConfigurationMap', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'user', type: 'address' }], + name: 'getUserEMode', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'asset', type: 'address' }], + name: 'getVirtualUnderlyingBalance', + outputs: [{ internalType: 'uint128', name: '', type: 'uint128' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'asset', type: 'address' }, + { internalType: 'address', name: 'aTokenAddress', type: 'address' }, + { internalType: 'address', name: 'variableDebtAddress', type: 'address' }, + { internalType: 'address', name: 'interestRateStrategyAddress', type: 'address' }, + ], + name: 'initReserve', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'contract IPoolAddressesProvider', name: 'provider', type: 'address' }, + ], + name: 'initialize', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'collateralAsset', type: 'address' }, + { internalType: 'address', name: 'debtAsset', type: 'address' }, + { internalType: 'address', name: 'user', type: 'address' }, + { internalType: 'uint256', name: 'debtToCover', type: 'uint256' }, + { internalType: 'bool', name: 'receiveAToken', type: 'bool' }, + ], + name: 'liquidationCall', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address[]', name: 'assets', type: 'address[]' }], + name: 'mintToTreasury', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'asset', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'address', name: 'onBehalfOf', type: 'address' }, + { internalType: 'uint16', name: 'referralCode', type: 'uint16' }, + ], + name: 'mintUnbacked', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'asset', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'uint256', name: 'interestRateMode', type: 'uint256' }, + { internalType: 'address', name: 'onBehalfOf', type: 'address' }, + ], + name: 'repay', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'asset', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'uint256', name: 'interestRateMode', type: 'uint256' }, + ], + name: 'repayWithATokens', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'asset', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'uint256', name: 'interestRateMode', type: 'uint256' }, + { internalType: 'address', name: 'onBehalfOf', 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: 'repayWithPermit', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'address', name: 'to', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + ], + name: 'rescueTokens', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'asset', type: 'address' }], + name: 'resetIsolationModeTotalDebt', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'asset', type: 'address' }, + { + components: [{ internalType: 'uint256', name: 'data', type: 'uint256' }], + internalType: 'struct DataTypes.ReserveConfigurationMap', + name: 'configuration', + type: 'tuple', + }, + ], + name: 'setConfiguration', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'asset', type: 'address' }, + { internalType: 'uint40', name: 'until', type: 'uint40' }, + ], + name: 'setLiquidationGracePeriod', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'asset', type: 'address' }, + { internalType: 'address', name: 'rateStrategyAddress', type: 'address' }, + ], + name: 'setReserveInterestRateStrategyAddress', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'uint8', name: 'categoryId', type: 'uint8' }], + name: 'setUserEMode', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'asset', type: 'address' }, + { internalType: 'bool', name: 'useAsCollateral', type: 'bool' }, + ], + name: 'setUserUseReserveAsCollateral', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'asset', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'address', name: 'onBehalfOf', type: 'address' }, + { internalType: 'uint16', name: 'referralCode', type: 'uint16' }, + ], + name: 'supply', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'asset', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'address', name: 'onBehalfOf', type: 'address' }, + { internalType: 'uint16', name: 'referralCode', type: 'uint16' }, + { 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: 'supplyWithPermit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'asset', type: 'address' }], + name: 'syncIndexesState', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'asset', type: 'address' }], + name: 'syncRatesState', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'uint256', name: 'protocolFee', type: 'uint256' }], + name: 'updateBridgeProtocolFee', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint128', name: 'flashLoanPremiumTotal', type: 'uint128' }, + { internalType: 'uint128', name: 'flashLoanPremiumToProtocol', type: 'uint128' }, + ], + name: 'updateFlashloanPremiums', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'asset', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'address', name: 'to', type: 'address' }, + ], + name: 'withdraw', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const; From 91dd5b068955a69ad42d03ae6809c18ee659526c Mon Sep 17 00:00:00 2001 From: defispartan Date: Tue, 10 Jun 2025 02:35:03 -0400 Subject: [PATCH 6/8] feat: add batch action to SupplyModal --- .../transactions/Supply/SupplyModalContent.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/components/transactions/Supply/SupplyModalContent.tsx b/src/components/transactions/Supply/SupplyModalContent.tsx index 54ac9552f9..0fb9fad9e4 100644 --- a/src/components/transactions/Supply/SupplyModalContent.tsx +++ b/src/components/transactions/Supply/SupplyModalContent.tsx @@ -51,6 +51,7 @@ import { getAssetCollateralType } from '../utils'; import { AAVEWarning } from '../Warnings/AAVEWarning'; import { IsolationModeWarning } from '../Warnings/IsolationModeWarning'; import { SNXWarning } from '../Warnings/SNXWarning'; +import { BatchSupplyActions } from './BatchSupplyActions'; import { SupplyActions } from './SupplyActions'; import { SupplyWrappedTokenActions } from './SupplyWrappedTokenActions'; @@ -146,11 +147,17 @@ export const SupplyModalContent = React.memo( }: SupplyModalContentProps) => { const { marketReferencePriceInUsd } = useAppDataContext(); const { mainTxState: supplyTxState, gasLimit, txError } = useModalContext(); - const [minRemainingBaseTokenBalance, currentMarketData, currentNetworkConfig] = useRootStore( + const [ + minRemainingBaseTokenBalance, + currentMarketData, + currentNetworkConfig, + walletCapabilities, + ] = useRootStore( useShallow((state) => [ state.poolComputed.minRemainingBaseTokenBalance, state.currentMarketData, state.currentNetworkConfig, + state.walletCapabilities, ]) ); @@ -272,7 +279,11 @@ export const SupplyModalContent = React.memo( {txError && } - + {walletCapabilities?.[currentMarketData.chainId] ? ( + + ) : ( + + )} ); } From 919f35649568c388e7460fde951a088966ebc82f Mon Sep 17 00:00:00 2001 From: defispartan Date: Tue, 10 Jun 2025 02:43:15 -0400 Subject: [PATCH 7/8] feat: method toggle styling --- .../ApprovalMethodToggleButton.tsx | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/components/transactions/FlowCommons/ApprovalMethodToggleButton.tsx b/src/components/transactions/FlowCommons/ApprovalMethodToggleButton.tsx index 151d76cdb6..92e1da5d1a 100644 --- a/src/components/transactions/FlowCommons/ApprovalMethodToggleButton.tsx +++ b/src/components/transactions/FlowCommons/ApprovalMethodToggleButton.tsx @@ -1,4 +1,5 @@ -import { SwitchHorizontalIcon } from '@heroicons/react/outline'; +import { CheckIcon } from '@heroicons/react/outline'; +import { CogIcon } from '@heroicons/react/solid'; import { Trans } from '@lingui/macro'; import { Box, @@ -45,13 +46,13 @@ export const ApprovalMethodToggleButton = ({ }} onClick={handleClick} > - + {currentMethod === ApprovalMethod.BATCH && Batch Transaction} {currentMethod === ApprovalMethod.PERMIT && Signature} {currentMethod === ApprovalMethod.APPROVE && Transaction} - - + + - - - + {currentMethod === ApprovalMethod.BATCH && } Batch Transaction @@ -108,9 +107,7 @@ export const ApprovalMethodToggleButton = ({ selected={currentMethod === ApprovalMethod.PERMIT} > - - - + {currentMethod === ApprovalMethod.PERMIT && } Signature @@ -121,9 +118,7 @@ export const ApprovalMethodToggleButton = ({ selected={currentMethod === ApprovalMethod.APPROVE} > - - - + {currentMethod === ApprovalMethod.APPROVE && } Transaction From 0d85bacdc37a15756bf49d2c23e414eb0a37c59d Mon Sep 17 00:00:00 2001 From: defispartan Date: Thu, 12 Jun 2025 21:09:38 -0400 Subject: [PATCH 8/8] feat: batch supply action --- .../ApprovalMethodToggleButton.tsx | 58 ++++++------------- .../Supply/BatchSupplyActions.tsx | 43 +++++++++----- 2 files changed, 48 insertions(+), 53 deletions(-) diff --git a/src/components/transactions/FlowCommons/ApprovalMethodToggleButton.tsx b/src/components/transactions/FlowCommons/ApprovalMethodToggleButton.tsx index 92e1da5d1a..5f68b23e1a 100644 --- a/src/components/transactions/FlowCommons/ApprovalMethodToggleButton.tsx +++ b/src/components/transactions/FlowCommons/ApprovalMethodToggleButton.tsx @@ -36,15 +36,8 @@ export const ApprovalMethodToggleButton = ({ return ( <> {currentMethod === ApprovalMethod.BATCH && Batch Transaction} @@ -59,39 +52,18 @@ export const ApprovalMethodToggleButton = ({ anchorEl={anchorEl} open={open} onClose={handleClose} - onClick={handleClose} - PaperProps={{ - elevation: 0, - sx: { - overflow: 'visible', - filter: 'drop-shadow(0px 2px 8px rgba(0,0,0,0.32))', - mt: 1.5, - '& .MuiAvatar-root': { - width: 32, - height: 32, - ml: -0.5, - mr: 1, - }, - '&:before': { - content: '""', - display: 'block', - position: 'absolute', - top: 0, - right: 14, - width: 10, - height: 10, - bgcolor: 'background.paper', - transform: 'translateY(-50%) rotate(45deg)', - zIndex: 0, - }, - }, + MenuListProps={{ + 'aria-labelledby': 'basic-button', }} - transformOrigin={{ horizontal: 'right', vertical: 'top' }} - anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} + keepMounted={true} + data-cy={`approveMenu_${currentMethod}`} > {showBatchOption && ( setMethod(ApprovalMethod.BATCH)} + onClick={() => { + setMethod(ApprovalMethod.BATCH); + handleClose(); + }} selected={currentMethod === ApprovalMethod.BATCH} > @@ -103,18 +75,24 @@ export const ApprovalMethodToggleButton = ({ )} setMethod(ApprovalMethod.PERMIT)} + onClick={() => { + setMethod(ApprovalMethod.PERMIT); + handleClose(); + }} selected={currentMethod === ApprovalMethod.PERMIT} > {currentMethod === ApprovalMethod.PERMIT && } - Signature + Signed Message setMethod(ApprovalMethod.APPROVE)} + onClick={() => { + setMethod(ApprovalMethod.APPROVE); + handleClose(); + }} selected={currentMethod === ApprovalMethod.APPROVE} > diff --git a/src/components/transactions/Supply/BatchSupplyActions.tsx b/src/components/transactions/Supply/BatchSupplyActions.tsx index e031fe5004..ce47a92ed8 100644 --- a/src/components/transactions/Supply/BatchSupplyActions.tsx +++ b/src/components/transactions/Supply/BatchSupplyActions.tsx @@ -116,8 +116,15 @@ export function BatchSupplyActions({ mutation: { onSuccess: (hash) => { console.log('BATCH HASH', hash); + addTransaction('batch', { + action: ProtocolAction.supply, + txState: 'success', + asset: poolAddress, + amount: amountToSupply, + assetName: symbol, + }); queryClient.invalidateQueries({ queryKey: queryKeysFactory.pool }); - setMainTxState({ success: true, txHash: hash.id }); + setMainTxState({ success: true, loading: false,txHash: hash.id }); }, onError: (error) => { console.log('ERROR', error); @@ -157,7 +164,7 @@ export function BatchSupplyActions({ const approveData = encodeFunctionData({ abi: erc20Abi, functionName: 'approve', - args: [poolAddress as `0x${string}`, parseUnits(amountToSupply, decimals)], + args: [currentMarketData.addresses.LENDING_POOL as `0x${string}`, parseUnits(amountToSupply, decimals)], }); calls.push({ to: poolAddress, @@ -176,7 +183,7 @@ export function BatchSupplyActions({ ], }); calls.push({ - to: poolAddress, + to: currentMarketData.addresses.LENDING_POOL, data: supplyData, }); @@ -200,6 +207,16 @@ export function BatchSupplyActions({ response = await sendTx(signedSupplyWithPermitTxData); await response.wait(1); + + addTransaction(response.hash, { + action: ProtocolAction.supplyWithPermit, + txState: 'success', + asset: poolAddress, + amount: amountToSupply, + assetName: symbol, + }); + + queryClient.invalidateQueries({ queryKey: queryKeysFactory.pool }); } else { action = ProtocolAction.supply; let supplyTxData = supply({ @@ -210,6 +227,16 @@ export function BatchSupplyActions({ response = await sendTx(supplyTxData); await response.wait(1); + + addTransaction(response.hash, { + action, + txState: 'success', + asset: poolAddress, + amount: amountToSupply, + assetName: symbol, + }); + + queryClient.invalidateQueries({ queryKey: queryKeysFactory.pool }); } setMainTxState({ @@ -217,16 +244,6 @@ export function BatchSupplyActions({ loading: false, success: true, }); - - addTransaction(response.hash, { - action, - txState: 'success', - asset: poolAddress, - amount: amountToSupply, - assetName: symbol, - }); - - queryClient.invalidateQueries({ queryKey: queryKeysFactory.pool }); } } catch (error) { const parsedError = getErrorTextFromError(error, TxAction.GAS_ESTIMATION, false);