UNPKG

@elizaos/plugin-aave

Version:

ElizaOS plugin for Aave Protocol - DeFi lending and borrowing

1,383 lines (1,377 loc) 112 kB
import { Service, elizaLogger, ModelType } from '@elizaos/core'; import { Pool, UiPoolDataProvider } from '@aave/contract-helpers'; import { formatReserves, formatUserSummary } from '@aave/math-utils'; import { ethers } from 'ethers'; import BigNumber3, { BigNumber } from 'bignumber.js'; import { AaveV3BaseSepolia, AaveV3OptimismSepolia, AaveV3ArbitrumSepolia, AaveV3Fuji, AaveV3Sepolia, AaveV3ZkSync, AaveV3Scroll, AaveV3Metis, AaveV3Gnosis, AaveV3BNB, AaveV3Base, AaveV3Optimism, AaveV3Arbitrum, AaveV3Avalanche, AaveV3Polygon, AaveV3Ethereum } from '@bgd-labs/aave-address-book'; // src/services/aave-service.ts // src/types/chains.ts var SupportedChain = /* @__PURE__ */ ((SupportedChain2) => { SupportedChain2["ETHEREUM"] = "ethereum"; SupportedChain2["POLYGON"] = "polygon"; SupportedChain2["AVALANCHE"] = "avalanche"; SupportedChain2["ARBITRUM"] = "arbitrum"; SupportedChain2["OPTIMISM"] = "optimism"; SupportedChain2["BASE"] = "base"; SupportedChain2["BNB"] = "bnb"; SupportedChain2["GNOSIS"] = "gnosis"; SupportedChain2["METIS"] = "metis"; SupportedChain2["SCROLL"] = "scroll"; SupportedChain2["ZKSYNC"] = "zksync"; SupportedChain2["SEPOLIA"] = "sepolia"; SupportedChain2["FUJI"] = "fuji"; SupportedChain2["ARBITRUM_SEPOLIA"] = "arbitrum-sepolia"; SupportedChain2["OPTIMISM_SEPOLIA"] = "optimism-sepolia"; SupportedChain2["BASE_SEPOLIA"] = "base-sepolia"; return SupportedChain2; })(SupportedChain || {}); var CHAIN_CONFIGS = { ["ethereum" /* ETHEREUM */]: { name: "Ethereum", chainId: 1, nativeCurrency: "ETH", wrappedNative: "WETH", defaultRpcUrl: "https://eth.llamarpc.com", isTestnet: false, popularAssets: ["USDC", "USDT", "DAI", "WETH", "WBTC", "LINK", "AAVE"] }, ["polygon" /* POLYGON */]: { name: "Polygon", chainId: 137, nativeCurrency: "MATIC", wrappedNative: "WMATIC", defaultRpcUrl: "https://polygon-rpc.com", isTestnet: false, popularAssets: ["USDC", "USDT", "DAI", "WMATIC", "WETH", "WBTC", "AAVE"] }, ["avalanche" /* AVALANCHE */]: { name: "Avalanche", chainId: 43114, nativeCurrency: "AVAX", wrappedNative: "WAVAX", defaultRpcUrl: "https://api.avax.network/ext/bc/C/rpc", isTestnet: false, popularAssets: ["USDC", "USDT", "DAI.e", "WAVAX", "WETH.e", "WBTC.e", "AAVE.e"] }, ["arbitrum" /* ARBITRUM */]: { name: "Arbitrum One", chainId: 42161, nativeCurrency: "ETH", wrappedNative: "WETH", defaultRpcUrl: "https://arb1.arbitrum.io/rpc", isTestnet: false, popularAssets: ["USDC", "USDT", "DAI", "WETH", "WBTC", "LINK", "ARB"] }, ["optimism" /* OPTIMISM */]: { name: "Optimism", chainId: 10, nativeCurrency: "ETH", wrappedNative: "WETH", defaultRpcUrl: "https://mainnet.optimism.io", isTestnet: false, popularAssets: ["USDC", "USDT", "DAI", "WETH", "WBTC", "LINK", "OP"] }, ["base" /* BASE */]: { name: "Base", chainId: 8453, nativeCurrency: "ETH", wrappedNative: "WETH", defaultRpcUrl: "https://mainnet.base.org", isTestnet: false, popularAssets: ["USDC", "DAI", "WETH", "cbETH", "AERO"] }, ["bnb" /* BNB */]: { name: "BNB Chain", chainId: 56, nativeCurrency: "BNB", wrappedNative: "WBNB", defaultRpcUrl: "https://bsc-dataseed1.binance.org", isTestnet: false, popularAssets: ["USDC", "USDT", "WBNB", "BTCB", "ETH", "FDUSD"] }, ["gnosis" /* GNOSIS */]: { name: "Gnosis Chain", chainId: 100, nativeCurrency: "xDAI", wrappedNative: "WXDAI", defaultRpcUrl: "https://rpc.gnosischain.com", isTestnet: false, popularAssets: ["USDC", "WXDAI", "WETH", "GNO"] }, ["metis" /* METIS */]: { name: "Metis", chainId: 1088, nativeCurrency: "METIS", wrappedNative: "WMETIS", defaultRpcUrl: "https://andromeda.metis.io/?owner=1088", isTestnet: false, popularAssets: ["m.USDC", "m.USDT", "m.DAI", "WMETIS", "METIS"] }, ["scroll" /* SCROLL */]: { name: "Scroll", chainId: 534352, nativeCurrency: "ETH", wrappedNative: "WETH", defaultRpcUrl: "https://rpc.scroll.io", isTestnet: false, popularAssets: ["USDC", "USDT", "WETH", "WBTC"] }, ["zksync" /* ZKSYNC */]: { name: "zkSync Era", chainId: 324, nativeCurrency: "ETH", wrappedNative: "WETH", defaultRpcUrl: "https://mainnet.era.zksync.io", isTestnet: false, popularAssets: ["USDC", "USDT", "WETH", "WBTC"] }, // Testnets ["sepolia" /* SEPOLIA */]: { name: "Ethereum Sepolia", chainId: 11155111, nativeCurrency: "ETH", wrappedNative: "WETH", defaultRpcUrl: "https://sepolia.infura.io/v3/demo", isTestnet: true, popularAssets: ["USDC", "USDT", "DAI", "WETH", "LINK", "AAVE"] }, ["fuji" /* FUJI */]: { name: "Avalanche Fuji", chainId: 43113, nativeCurrency: "AVAX", wrappedNative: "WAVAX", defaultRpcUrl: "https://api.avax-test.network/ext/bc/C/rpc", isTestnet: true, popularAssets: ["USDC", "WAVAX", "WETH.e"] }, ["arbitrum-sepolia" /* ARBITRUM_SEPOLIA */]: { name: "Arbitrum Sepolia", chainId: 421614, nativeCurrency: "ETH", wrappedNative: "WETH", defaultRpcUrl: "https://sepolia-rollup.arbitrum.io/rpc", isTestnet: true, popularAssets: ["USDC", "WETH"] }, ["optimism-sepolia" /* OPTIMISM_SEPOLIA */]: { name: "Optimism Sepolia", chainId: 11155420, nativeCurrency: "ETH", wrappedNative: "WETH", defaultRpcUrl: "https://sepolia.optimism.io", isTestnet: true, popularAssets: ["USDC", "WETH"] }, ["base-sepolia" /* BASE_SEPOLIA */]: { name: "Base Sepolia", chainId: 84532, nativeCurrency: "ETH", wrappedNative: "WETH", defaultRpcUrl: "https://sepolia.base.org", isTestnet: true, popularAssets: ["USDC", "WETH"] } }; var InterestRateMode = /* @__PURE__ */ ((InterestRateMode2) => { InterestRateMode2[InterestRateMode2["STABLE"] = 1] = "STABLE"; InterestRateMode2[InterestRateMode2["VARIABLE"] = 2] = "VARIABLE"; return InterestRateMode2; })(InterestRateMode || {}); var AaveErrorCode = /* @__PURE__ */ ((AaveErrorCode2) => { AaveErrorCode2["UNKNOWN"] = "UNKNOWN"; AaveErrorCode2["INSUFFICIENT_BALANCE"] = "INSUFFICIENT_BALANCE"; AaveErrorCode2["INSUFFICIENT_COLLATERAL"] = "INSUFFICIENT_COLLATERAL"; AaveErrorCode2["ASSET_NOT_SUPPORTED"] = "ASSET_NOT_SUPPORTED"; AaveErrorCode2["BORROWING_NOT_ENABLED"] = "BORROWING_NOT_ENABLED"; AaveErrorCode2["STABLE_BORROWING_NOT_ENABLED"] = "STABLE_BORROWING_NOT_ENABLED"; AaveErrorCode2["HEALTH_FACTOR_TOO_LOW"] = "HEALTH_FACTOR_TOO_LOW"; AaveErrorCode2["TRANSACTION_FAILED"] = "TRANSACTION_FAILED"; AaveErrorCode2["INVALID_PARAMETERS"] = "INVALID_PARAMETERS"; AaveErrorCode2["USER_HAS_STABLE_RATE_LOAN"] = "USER_HAS_STABLE_RATE_LOAN"; AaveErrorCode2["AMOUNT_TOO_HIGH"] = "AMOUNT_TOO_HIGH"; AaveErrorCode2["POOL_PAUSED"] = "POOL_PAUSED"; AaveErrorCode2["RESERVE_FROZEN"] = "RESERVE_FROZEN"; AaveErrorCode2["RESERVE_INACTIVE"] = "RESERVE_INACTIVE"; AaveErrorCode2["INITIALIZATION_FAILED"] = "INITIALIZATION_FAILED"; AaveErrorCode2["CONNECTION_FAILED"] = "CONNECTION_FAILED"; AaveErrorCode2["SERVICE_NOT_INITIALIZED"] = "SERVICE_NOT_INITIALIZED"; AaveErrorCode2["WALLET_NOT_CONNECTED"] = "WALLET_NOT_CONNECTED"; AaveErrorCode2["TRANSACTION_GENERATION_FAILED"] = "TRANSACTION_GENERATION_FAILED"; AaveErrorCode2["SUPPLY_FAILED"] = "SUPPLY_FAILED"; AaveErrorCode2["WITHDRAW_FAILED"] = "WITHDRAW_FAILED"; AaveErrorCode2["BORROW_FAILED"] = "BORROW_FAILED"; AaveErrorCode2["REPAY_FAILED"] = "REPAY_FAILED"; AaveErrorCode2["DATA_FETCH_FAILED"] = "DATA_FETCH_FAILED"; AaveErrorCode2["ASSET_NOT_FOUND"] = "ASSET_NOT_FOUND"; AaveErrorCode2["UNSUPPORTED_ASSET"] = "UNSUPPORTED_ASSET"; AaveErrorCode2["NO_BORROW_CAPACITY"] = "NO_BORROW_CAPACITY"; AaveErrorCode2["UNSUPPORTED_OPERATION"] = "UNSUPPORTED_OPERATION"; return AaveErrorCode2; })(AaveErrorCode || {}); var AaveError = class extends Error { /** Error code for programmatic handling */ code; /** Original error that caused this error */ cause; /** Additional context about the error */ context; constructor(message, code = "UNKNOWN" /* UNKNOWN */, cause, context) { super(message); this.name = "AaveError"; this.code = code; this.cause = cause; this.context = context; } }; var AAVE_CONSTANTS = { /** Maximum uint256 value for unlimited approvals/withdrawals */ MAX_UINT256: new BigNumber("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), /** Number of seconds in a year for APY calculations */ SECONDS_PER_YEAR: new BigNumber(31536e3), /** Number of ray units (1e27) used by Aave for rate calculations */ RAY: new BigNumber("1000000000000000000000000000"), /** Number of wei units (1e18) */ WAD: new BigNumber("1000000000000000000"), /** Percentage multiplier (100 for percentage) */ PERCENTAGE_FACTOR: new BigNumber(1e4) }; function isAaveError(error) { return error instanceof AaveError; } function isValidInterestRateMode(mode) { return mode === 1 /* STABLE */ || mode === 2 /* VARIABLE */; } // src/utils/chain-resolver.ts var CHAIN_ADDRESS_MAP = { // Mainnets ["ethereum" /* ETHEREUM */]: { POOL: AaveV3Ethereum.POOL, POOL_ADDRESSES_PROVIDER: AaveV3Ethereum.POOL_ADDRESSES_PROVIDER, AAVE_PROTOCOL_DATA_PROVIDER: AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER, UI_POOL_DATA_PROVIDER: AaveV3Ethereum.UI_POOL_DATA_PROVIDER, WETH_GATEWAY: AaveV3Ethereum.WETH_GATEWAY, ACL_MANAGER: AaveV3Ethereum.ACL_MANAGER, COLLECTOR: AaveV3Ethereum.COLLECTOR, ORACLE: AaveV3Ethereum.ORACLE }, ["polygon" /* POLYGON */]: { POOL: AaveV3Polygon.POOL, POOL_ADDRESSES_PROVIDER: AaveV3Polygon.POOL_ADDRESSES_PROVIDER, AAVE_PROTOCOL_DATA_PROVIDER: AaveV3Polygon.AAVE_PROTOCOL_DATA_PROVIDER, UI_POOL_DATA_PROVIDER: AaveV3Polygon.UI_POOL_DATA_PROVIDER, WETH_GATEWAY: AaveV3Polygon.WETH_GATEWAY, ACL_MANAGER: AaveV3Polygon.ACL_MANAGER, COLLECTOR: AaveV3Polygon.COLLECTOR, ORACLE: AaveV3Polygon.ORACLE }, ["avalanche" /* AVALANCHE */]: { POOL: AaveV3Avalanche.POOL, POOL_ADDRESSES_PROVIDER: AaveV3Avalanche.POOL_ADDRESSES_PROVIDER, AAVE_PROTOCOL_DATA_PROVIDER: AaveV3Avalanche.AAVE_PROTOCOL_DATA_PROVIDER, UI_POOL_DATA_PROVIDER: AaveV3Avalanche.UI_POOL_DATA_PROVIDER, WETH_GATEWAY: AaveV3Avalanche.WETH_GATEWAY, ACL_MANAGER: AaveV3Avalanche.ACL_MANAGER, COLLECTOR: AaveV3Avalanche.COLLECTOR, ORACLE: AaveV3Avalanche.ORACLE }, ["arbitrum" /* ARBITRUM */]: { POOL: AaveV3Arbitrum.POOL, POOL_ADDRESSES_PROVIDER: AaveV3Arbitrum.POOL_ADDRESSES_PROVIDER, AAVE_PROTOCOL_DATA_PROVIDER: AaveV3Arbitrum.AAVE_PROTOCOL_DATA_PROVIDER, UI_POOL_DATA_PROVIDER: AaveV3Arbitrum.UI_POOL_DATA_PROVIDER, WETH_GATEWAY: AaveV3Arbitrum.WETH_GATEWAY, ACL_MANAGER: AaveV3Arbitrum.ACL_MANAGER, COLLECTOR: AaveV3Arbitrum.COLLECTOR, ORACLE: AaveV3Arbitrum.ORACLE }, ["optimism" /* OPTIMISM */]: { POOL: AaveV3Optimism.POOL, POOL_ADDRESSES_PROVIDER: AaveV3Optimism.POOL_ADDRESSES_PROVIDER, AAVE_PROTOCOL_DATA_PROVIDER: AaveV3Optimism.AAVE_PROTOCOL_DATA_PROVIDER, UI_POOL_DATA_PROVIDER: AaveV3Optimism.UI_POOL_DATA_PROVIDER, WETH_GATEWAY: AaveV3Optimism.WETH_GATEWAY, ACL_MANAGER: AaveV3Optimism.ACL_MANAGER, COLLECTOR: AaveV3Optimism.COLLECTOR, ORACLE: AaveV3Optimism.ORACLE }, ["base" /* BASE */]: { POOL: AaveV3Base.POOL, POOL_ADDRESSES_PROVIDER: AaveV3Base.POOL_ADDRESSES_PROVIDER, AAVE_PROTOCOL_DATA_PROVIDER: AaveV3Base.AAVE_PROTOCOL_DATA_PROVIDER, UI_POOL_DATA_PROVIDER: AaveV3Base.UI_POOL_DATA_PROVIDER, WETH_GATEWAY: AaveV3Base.WETH_GATEWAY, ACL_MANAGER: AaveV3Base.ACL_MANAGER, COLLECTOR: AaveV3Base.COLLECTOR, ORACLE: AaveV3Base.ORACLE }, ["bnb" /* BNB */]: { POOL: AaveV3BNB.POOL, POOL_ADDRESSES_PROVIDER: AaveV3BNB.POOL_ADDRESSES_PROVIDER, AAVE_PROTOCOL_DATA_PROVIDER: AaveV3BNB.AAVE_PROTOCOL_DATA_PROVIDER, UI_POOL_DATA_PROVIDER: AaveV3BNB.UI_POOL_DATA_PROVIDER, WETH_GATEWAY: AaveV3BNB.WETH_GATEWAY, ACL_MANAGER: AaveV3BNB.ACL_MANAGER, COLLECTOR: AaveV3BNB.COLLECTOR, ORACLE: AaveV3BNB.ORACLE }, ["gnosis" /* GNOSIS */]: { POOL: AaveV3Gnosis.POOL, POOL_ADDRESSES_PROVIDER: AaveV3Gnosis.POOL_ADDRESSES_PROVIDER, AAVE_PROTOCOL_DATA_PROVIDER: AaveV3Gnosis.AAVE_PROTOCOL_DATA_PROVIDER, UI_POOL_DATA_PROVIDER: AaveV3Gnosis.UI_POOL_DATA_PROVIDER, WETH_GATEWAY: AaveV3Gnosis.WETH_GATEWAY, ACL_MANAGER: AaveV3Gnosis.ACL_MANAGER, COLLECTOR: AaveV3Gnosis.COLLECTOR, ORACLE: AaveV3Gnosis.ORACLE }, ["metis" /* METIS */]: { POOL: AaveV3Metis.POOL, POOL_ADDRESSES_PROVIDER: AaveV3Metis.POOL_ADDRESSES_PROVIDER, AAVE_PROTOCOL_DATA_PROVIDER: AaveV3Metis.AAVE_PROTOCOL_DATA_PROVIDER, UI_POOL_DATA_PROVIDER: AaveV3Metis.UI_POOL_DATA_PROVIDER, // WETH_GATEWAY: Not available on Metis ACL_MANAGER: AaveV3Metis.ACL_MANAGER, COLLECTOR: AaveV3Metis.COLLECTOR, ORACLE: AaveV3Metis.ORACLE }, ["scroll" /* SCROLL */]: { POOL: AaveV3Scroll.POOL, POOL_ADDRESSES_PROVIDER: AaveV3Scroll.POOL_ADDRESSES_PROVIDER, AAVE_PROTOCOL_DATA_PROVIDER: AaveV3Scroll.AAVE_PROTOCOL_DATA_PROVIDER, UI_POOL_DATA_PROVIDER: AaveV3Scroll.UI_POOL_DATA_PROVIDER, WETH_GATEWAY: AaveV3Scroll.WETH_GATEWAY, ACL_MANAGER: AaveV3Scroll.ACL_MANAGER, COLLECTOR: AaveV3Scroll.COLLECTOR, ORACLE: AaveV3Scroll.ORACLE }, ["zksync" /* ZKSYNC */]: { POOL: AaveV3ZkSync.POOL, POOL_ADDRESSES_PROVIDER: AaveV3ZkSync.POOL_ADDRESSES_PROVIDER, AAVE_PROTOCOL_DATA_PROVIDER: AaveV3ZkSync.AAVE_PROTOCOL_DATA_PROVIDER, UI_POOL_DATA_PROVIDER: AaveV3ZkSync.UI_POOL_DATA_PROVIDER, WETH_GATEWAY: AaveV3ZkSync.WETH_GATEWAY, ACL_MANAGER: AaveV3ZkSync.ACL_MANAGER, COLLECTOR: AaveV3ZkSync.COLLECTOR, ORACLE: AaveV3ZkSync.ORACLE }, // Testnets ["sepolia" /* SEPOLIA */]: { POOL: AaveV3Sepolia.POOL, POOL_ADDRESSES_PROVIDER: AaveV3Sepolia.POOL_ADDRESSES_PROVIDER, AAVE_PROTOCOL_DATA_PROVIDER: AaveV3Sepolia.AAVE_PROTOCOL_DATA_PROVIDER, UI_POOL_DATA_PROVIDER: AaveV3Sepolia.UI_POOL_DATA_PROVIDER, WETH_GATEWAY: AaveV3Sepolia.WETH_GATEWAY, ACL_MANAGER: AaveV3Sepolia.ACL_MANAGER, COLLECTOR: AaveV3Sepolia.COLLECTOR, ORACLE: AaveV3Sepolia.ORACLE }, ["fuji" /* FUJI */]: { POOL: AaveV3Fuji.POOL, POOL_ADDRESSES_PROVIDER: AaveV3Fuji.POOL_ADDRESSES_PROVIDER, AAVE_PROTOCOL_DATA_PROVIDER: AaveV3Fuji.AAVE_PROTOCOL_DATA_PROVIDER, // UI_POOL_DATA_PROVIDER: Not available on Fuji WETH_GATEWAY: AaveV3Fuji.WETH_GATEWAY, ACL_MANAGER: AaveV3Fuji.ACL_MANAGER, COLLECTOR: AaveV3Fuji.COLLECTOR, ORACLE: AaveV3Fuji.ORACLE }, ["arbitrum-sepolia" /* ARBITRUM_SEPOLIA */]: { POOL: AaveV3ArbitrumSepolia.POOL, POOL_ADDRESSES_PROVIDER: AaveV3ArbitrumSepolia.POOL_ADDRESSES_PROVIDER, AAVE_PROTOCOL_DATA_PROVIDER: AaveV3ArbitrumSepolia.AAVE_PROTOCOL_DATA_PROVIDER, UI_POOL_DATA_PROVIDER: AaveV3ArbitrumSepolia.UI_POOL_DATA_PROVIDER, WETH_GATEWAY: AaveV3ArbitrumSepolia.WETH_GATEWAY, ACL_MANAGER: AaveV3ArbitrumSepolia.ACL_MANAGER, COLLECTOR: AaveV3ArbitrumSepolia.COLLECTOR, ORACLE: AaveV3ArbitrumSepolia.ORACLE }, ["optimism-sepolia" /* OPTIMISM_SEPOLIA */]: { POOL: AaveV3OptimismSepolia.POOL, POOL_ADDRESSES_PROVIDER: AaveV3OptimismSepolia.POOL_ADDRESSES_PROVIDER, AAVE_PROTOCOL_DATA_PROVIDER: AaveV3OptimismSepolia.AAVE_PROTOCOL_DATA_PROVIDER, UI_POOL_DATA_PROVIDER: AaveV3OptimismSepolia.UI_POOL_DATA_PROVIDER, WETH_GATEWAY: AaveV3OptimismSepolia.WETH_GATEWAY, ACL_MANAGER: AaveV3OptimismSepolia.ACL_MANAGER, COLLECTOR: AaveV3OptimismSepolia.COLLECTOR, ORACLE: AaveV3OptimismSepolia.ORACLE }, ["base-sepolia" /* BASE_SEPOLIA */]: { POOL: AaveV3BaseSepolia.POOL, POOL_ADDRESSES_PROVIDER: AaveV3BaseSepolia.POOL_ADDRESSES_PROVIDER, AAVE_PROTOCOL_DATA_PROVIDER: AaveV3BaseSepolia.AAVE_PROTOCOL_DATA_PROVIDER, UI_POOL_DATA_PROVIDER: AaveV3BaseSepolia.UI_POOL_DATA_PROVIDER, WETH_GATEWAY: AaveV3BaseSepolia.WETH_GATEWAY, ACL_MANAGER: AaveV3BaseSepolia.ACL_MANAGER, COLLECTOR: AaveV3BaseSepolia.COLLECTOR, ORACLE: AaveV3BaseSepolia.ORACLE } }; function resolveChainContext(chainName) { const normalizedChain = chainName.toLowerCase().trim(); if (!Object.values(SupportedChain).includes(normalizedChain)) { throw new AaveError( `Unsupported chain: ${chainName}. Supported chains: ${Object.values(SupportedChain).join(", ")}`, "INVALID_PARAMETERS" /* INVALID_PARAMETERS */ ); } const config = CHAIN_CONFIGS[normalizedChain]; if (!config) { throw new AaveError( `Chain configuration not found for: ${chainName}`, "INVALID_PARAMETERS" /* INVALID_PARAMETERS */ ); } const addresses = CHAIN_ADDRESS_MAP[normalizedChain]; if (!addresses) { throw new AaveError( `Aave V3 contracts not available on chain: ${chainName}`, "INVALID_PARAMETERS" /* INVALID_PARAMETERS */ ); } return { chain: normalizedChain, config, addresses }; } function resolveRpcUrl(chainContext, userRpcUrl) { if (userRpcUrl) { return userRpcUrl; } return chainContext.config.defaultRpcUrl; } function isMaxAmount(amount) { if (typeof amount === "string") { const normalized = amount.toLowerCase().trim(); return ["max", "maximum", "all", "everything"].includes(normalized); } if (amount instanceof BigNumber3) { return false; } return false; } function parseAmount(amount) { if (amount === null || amount === void 0) { throw new Error("Amount cannot be null or undefined"); } if (typeof amount === "string") { const trimmed = amount.trim(); if (!trimmed) { throw new Error("Amount cannot be empty"); } if (trimmed === "NaN" || trimmed === "Infinity" || trimmed === "-Infinity") { throw new Error("Invalid amount format"); } const num2 = new BigNumber3(trimmed); if (num2.isNaN() || !num2.isFinite()) { throw new Error("Invalid amount format"); } if (num2.isLessThanOrEqualTo(0)) { throw new Error("Amount must be greater than 0"); } return num2; } if (typeof amount === "number") { if (!isFinite(amount) || isNaN(amount)) { throw new Error("Amount must be a finite number"); } } const num = new BigNumber3(amount); if (num.isNaN() || !num.isFinite() || num.isLessThanOrEqualTo(0)) { throw new Error("Amount must be a positive finite number"); } return num; } function validateAddress(address) { if (!address || typeof address !== "string") { throw new Error("Address is required"); } const trimmed = address.trim(); if (!trimmed.match(/^0x[a-fA-F0-9]{40}$/i)) { throw new Error("Invalid address: must be a valid Ethereum address"); } return trimmed; } function validateSupplyParams(params) { if (!params.user || !params.asset || !params.amount) { throw new Error("Missing required parameters: user, asset, amount"); } return { user: validateAddress(params.user), asset: params.asset.trim(), amount: isMaxAmount(params.amount) ? params.amount : parseAmount(params.amount).toString() }; } function validateWithdrawParams(params) { if (!params.user || !params.asset || !params.amount) { throw new Error("Missing required parameters: user, asset, amount"); } return { user: validateAddress(params.user), asset: params.asset.trim(), amount: isMaxAmount(params.amount) ? params.amount : parseAmount(params.amount).toString() }; } function validateBorrowParams(params) { if (!params.user || !params.asset || !params.amount) { throw new Error("Missing required parameters: user, asset, amount"); } if (params.interestRateMode !== void 0) { if (params.interestRateMode !== 1 /* STABLE */ && params.interestRateMode !== 2 /* VARIABLE */) { throw new Error("Invalid interest rate mode: must be 1 (stable) or 2 (variable)"); } } return { user: validateAddress(params.user), asset: params.asset.trim(), amount: parseAmount(params.amount).toString(), interestRateMode: params.interestRateMode || 2 /* VARIABLE */ }; } function validateRepayParams(params) { if (!params.user || !params.asset || !params.amount) { throw new Error("Missing required parameters: user, asset, amount"); } return { user: validateAddress(params.user), asset: params.asset.trim(), amount: isMaxAmount(params.amount) ? params.amount : parseAmount(params.amount).toString(), interestRateMode: params.interestRateMode || 2 /* VARIABLE */ }; } // src/utils/error-handler.ts function getRateModeName(mode) { switch (mode) { case 1 /* STABLE */: return "Stable"; case 2 /* VARIABLE */: return "Variable"; default: return "Unknown"; } } var AAVE_PROTOCOL_ERRORS = { // Pool errors (P prefix) "P_INVALID_FLASHLOAN_EXECUTOR_RETURN": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, description: "Invalid flashloan executor return", userMessage: "Flash loan execution failed. Please check your flash loan logic.", recovery: { action: "Review and fix your flash loan implementation", requiresUserAction: true } }, "P_VT_TRANSFER_NOT_SUPPORTED": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, description: "Variable debt token transfer not supported", userMessage: "Cannot transfer debt tokens. Use repay and borrow operations instead.", recovery: { action: "Use repay() and borrow() functions for debt management", requiresUserAction: true } }, "P_ASSET_NOT_LISTED": { code: "ASSET_NOT_SUPPORTED" /* ASSET_NOT_SUPPORTED */, description: "Asset is not listed in the pool", userMessage: "This asset is not supported by Aave protocol.", recovery: { action: "Use a supported asset from the Aave markets", requiresUserAction: true } }, "P_INVALID_AMOUNT": { code: "INVALID_PARAMETERS" /* INVALID_PARAMETERS */, description: "Invalid amount provided", userMessage: "The amount provided is invalid. Please check the amount and try again.", recovery: { action: "Ensure amount is positive and within acceptable range", requiresUserAction: true } }, // Reserve errors (R prefix) "R_LIQUIDITY_INDEX_OVERFLOW": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, description: "Liquidity index overflow", userMessage: "Internal calculation error. Please try again later.", recovery: { action: "Wait and retry the operation", requiresUserAction: false } }, "R_VARIABLE_BORROW_INDEX_OVERFLOW": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, description: "Variable borrow index overflow", userMessage: "Internal calculation error. Please try again later.", recovery: { action: "Wait and retry the operation", requiresUserAction: false } }, "R_LIQUIDITY_RATE_OVERFLOW": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, description: "Liquidity rate overflow", userMessage: "Interest rate calculation error. Please try again later.", recovery: { action: "Wait and retry the operation", requiresUserAction: false } }, "R_VARIABLE_BORROW_RATE_OVERFLOW": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, description: "Variable borrow rate overflow", userMessage: "Interest rate calculation error. Please try again later.", recovery: { action: "Wait and retry the operation", requiresUserAction: false } }, // Validation errors (V prefix) "V_INCONSISTENT_FLASHLOAN_PARAMS": { code: "INVALID_PARAMETERS" /* INVALID_PARAMETERS */, description: "Inconsistent flashloan parameters", userMessage: "Flash loan parameters are inconsistent.", recovery: { action: "Check flash loan asset and amount parameters", requiresUserAction: true } }, "V_COLLATERAL_BALANCE_IS_ZERO": { code: "INSUFFICIENT_COLLATERAL" /* INSUFFICIENT_COLLATERAL */, description: "Collateral balance is zero", userMessage: "You have no collateral deposited. Please supply collateral first.", recovery: { action: "Supply assets as collateral before borrowing", requiresUserAction: true } }, "V_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD": { code: "HEALTH_FACTOR_TOO_LOW" /* HEALTH_FACTOR_TOO_LOW */, description: "Health factor below liquidation threshold", userMessage: "This operation would put your position at risk of liquidation.", recovery: { action: "Supply more collateral or reduce borrowing amount", requiresUserAction: true } }, "V_COLLATERAL_CANNOT_COVER_NEW_BORROW": { code: "INSUFFICIENT_COLLATERAL" /* INSUFFICIENT_COLLATERAL */, description: "Insufficient collateral for new borrow", userMessage: "You don't have enough collateral to borrow this amount.", recovery: { action: "Supply more collateral or reduce borrow amount", requiresUserAction: true } }, "V_STABLE_BORROWING_NOT_ENABLED": { code: "STABLE_BORROWING_NOT_ENABLED" /* STABLE_BORROWING_NOT_ENABLED */, description: "Stable borrowing not enabled for asset", userMessage: "Stable rate borrowing is not available for this asset.", recovery: { action: "Use variable rate borrowing instead", requiresUserAction: true } }, "V_NO_DEBT_OF_SELECTED_TYPE": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, description: "No debt of selected type", userMessage: "You don't have debt in the selected interest rate mode.", recovery: { action: "Check your debt positions and select the correct rate mode", requiresUserAction: true } }, "V_NO_STABLE_RATE_LOAN_IN_RESERVE": { code: "STABLE_BORROWING_NOT_ENABLED" /* STABLE_BORROWING_NOT_ENABLED */, description: "No stable rate loan exists", userMessage: "No stable rate loan exists for this asset.", recovery: { action: "Use variable rate borrowing", requiresUserAction: true } }, "V_NO_VARIABLE_RATE_LOAN_IN_RESERVE": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, description: "No variable rate loan exists", userMessage: "No variable rate loan exists for this asset.", recovery: { action: "Check your borrow positions", requiresUserAction: true } }, "V_UNDERLYING_BALANCE_ZERO": { code: "INSUFFICIENT_BALANCE" /* INSUFFICIENT_BALANCE */, description: "Underlying balance is zero", userMessage: "You don't have any balance of this asset to supply.", recovery: { action: "Acquire the asset first or check your balance", requiresUserAction: true } }, "V_INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, description: "Interest rate rebalance conditions not met", userMessage: "Cannot rebalance interest rate. Conditions not met.", recovery: { action: "Try again later when market conditions change", requiresUserAction: false } }, // Asset configuration errors (A prefix) "A_INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, description: "Rebalance conditions not met", userMessage: "Cannot rebalance stable rate. Market conditions not suitable.", recovery: { action: "Try again when utilization rate changes", requiresUserAction: false } }, "A_NO_MORE_RESERVES_ALLOWED": { code: "POOL_PAUSED" /* POOL_PAUSED */, description: "No more reserves allowed", userMessage: "Maximum number of assets reached in the pool.", recovery: { action: "Contact Aave governance to add more reserves", requiresUserAction: true } }, // Supply/Withdraw specific "S_NOT_ENOUGH_AVAILABLE_USER_BALANCE": { code: "INSUFFICIENT_BALANCE" /* INSUFFICIENT_BALANCE */, description: "Insufficient user balance", userMessage: "You don't have enough balance to complete this operation.", recovery: { action: "Check your wallet balance and reduce the amount", requiresUserAction: true } }, "W_NO_ATOKEN_BALANCE": { code: "INSUFFICIENT_BALANCE" /* INSUFFICIENT_BALANCE */, description: "No aToken balance to withdraw", userMessage: "You don't have any supplied balance to withdraw.", recovery: { action: "Check your supplied positions", requiresUserAction: true } }, // Borrow/Repay specific "B_BORROW_CAP_EXCEEDED": { code: "AMOUNT_TOO_HIGH" /* AMOUNT_TOO_HIGH */, description: "Borrow cap exceeded", userMessage: "Borrowing this amount would exceed the protocol's borrow cap.", recovery: { action: "Reduce the borrow amount", requiresUserAction: true } }, "B_SUPPLY_CAP_EXCEEDED": { code: "AMOUNT_TOO_HIGH" /* AMOUNT_TOO_HIGH */, description: "Supply cap exceeded", userMessage: "Supplying this amount would exceed the protocol's supply cap.", recovery: { action: "Reduce the supply amount", requiresUserAction: true } }, // Reserve status errors "RESERVE_INACTIVE": { code: "RESERVE_INACTIVE" /* RESERVE_INACTIVE */, description: "Reserve is inactive", userMessage: "This asset is currently inactive in the Aave protocol.", recovery: { action: "Choose an active asset or wait for reactivation", requiresUserAction: true } }, "RESERVE_FROZEN": { code: "RESERVE_FROZEN" /* RESERVE_FROZEN */, description: "Reserve is frozen", userMessage: "This asset is currently frozen. Only repay and withdraw operations are allowed.", recovery: { action: "Choose an unfrozen asset or wait for unfreeze", requiresUserAction: true } }, "RESERVE_PAUSED": { code: "POOL_PAUSED" /* POOL_PAUSED */, description: "Reserve is paused", userMessage: "Operations on this asset are temporarily paused.", recovery: { action: "Wait for the asset to be unpaused", requiresUserAction: false } } }; var NETWORK_ERROR_PATTERNS = { // RPC errors "network timeout": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, userMessage: "Network connection timed out. Please try again.", isRetryable: true }, "connection refused": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, userMessage: "Unable to connect to the network. Please check your connection.", isRetryable: true }, "socket hang up": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, userMessage: "Network connection interrupted. Please try again.", isRetryable: true }, "request timeout": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, userMessage: "Request timed out. Please try again.", isRetryable: true }, "rate limit": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, userMessage: "Too many requests. Please wait a moment and try again.", isRetryable: true }, "insufficient funds for gas": { code: "INSUFFICIENT_BALANCE" /* INSUFFICIENT_BALANCE */, userMessage: "Insufficient ETH for gas fees. Please add more ETH to your wallet.", isRetryable: false }, "gas limit exceeded": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, userMessage: "Transaction requires too much gas. Try reducing the amount.", isRetryable: false }, "replacement transaction underpriced": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, userMessage: "Transaction replacement fee too low. Increase gas price.", isRetryable: true }, "nonce too low": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, userMessage: "Transaction nonce is outdated. Please refresh and try again.", isRetryable: true }, "already known": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, userMessage: "Transaction already pending. Please wait for confirmation.", isRetryable: false } }; var GAS_ERROR_PATTERNS = { "execution reverted": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, userMessage: "Transaction would fail. Please check your parameters.", recovery: { action: "Review transaction parameters and account balances", requiresUserAction: true } }, "out of gas": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, userMessage: "Transaction ran out of gas. Increase gas limit.", recovery: { action: "Increase gas limit or reduce transaction complexity", requiresUserAction: true } }, "invalid opcode": { code: "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, userMessage: "Invalid transaction. Please check your parameters.", recovery: { action: "Verify all transaction parameters are correct", requiresUserAction: true } } }; function mapAaveError(error, context = {}) { const errorMessage = error.message.toLowerCase(); for (const [pattern, errorInfo] of Object.entries(AAVE_PROTOCOL_ERRORS)) { if (errorMessage.includes(pattern.toLowerCase()) || errorMessage.includes(`execution reverted: ${pattern.toLowerCase()}`)) { return new AaveError( errorInfo.userMessage, errorInfo.code, error, { ...context, protocolError: pattern, recovery: errorInfo.recovery } ); } } for (const [pattern, errorInfo] of Object.entries(NETWORK_ERROR_PATTERNS)) { if (errorMessage.includes(pattern)) { return new AaveError( errorInfo.userMessage, errorInfo.code, error, { ...context, networkError: pattern, isRetryable: errorInfo.isRetryable } ); } } for (const [pattern, errorInfo] of Object.entries(GAS_ERROR_PATTERNS)) { if (errorMessage.includes(pattern)) { return new AaveError( errorInfo.userMessage, errorInfo.code, error, { ...context, gasError: pattern, recovery: errorInfo.recovery } ); } } if (errorMessage.includes("insufficient")) { if (errorMessage.includes("balance") || errorMessage.includes("funds")) { return new AaveError( "Insufficient balance to complete this operation.", "INSUFFICIENT_BALANCE" /* INSUFFICIENT_BALANCE */, error, context ); } if (errorMessage.includes("collateral")) { return new AaveError( "Insufficient collateral for this operation.", "INSUFFICIENT_COLLATERAL" /* INSUFFICIENT_COLLATERAL */, error, context ); } } if (errorMessage.includes("health factor")) { return new AaveError( "This operation would put your position at risk of liquidation.", "HEALTH_FACTOR_TOO_LOW" /* HEALTH_FACTOR_TOO_LOW */, error, context ); } if (errorMessage.includes("not enabled") && errorMessage.includes("stable")) { return new AaveError( "Stable rate borrowing is not enabled for this asset.", "STABLE_BORROWING_NOT_ENABLED" /* STABLE_BORROWING_NOT_ENABLED */, error, context ); } if (errorMessage.includes("paused")) { return new AaveError( "The pool or asset is currently paused.", "POOL_PAUSED" /* POOL_PAUSED */, error, context ); } if (errorMessage.includes("frozen")) { return new AaveError( "The asset is currently frozen.", "RESERVE_FROZEN" /* RESERVE_FROZEN */, error, context ); } return new AaveError( "An unexpected error occurred. Please try again.", "UNKNOWN" /* UNKNOWN */, error, context ); } function handleContractError(error, contractName, methodName, context = {}) { const enhancedContext = { ...context, contractName, methodName }; const aaveError = mapAaveError(error, enhancedContext); return { originalError: error, aaveError, userMessage: aaveError.message, technicalMessage: `Contract error in ${contractName}.${methodName}: ${error.message}`, context: enhancedContext, isRetryable: determineRetryability(aaveError), logLevel: determineLogLevel(aaveError), recovery: aaveError.context?.recovery }; } function handleTransactionError(error, transactionHash, context = {}) { const enhancedContext = { ...context, transactionHash }; const aaveError = mapAaveError(error, enhancedContext); let userMessage = aaveError.message; let technicalMessage = error.message; if (aaveError.code === "INSUFFICIENT_BALANCE" /* INSUFFICIENT_BALANCE */ && context.operation) { const operation = context.operation; const asset = context.asset || "the asset"; switch (operation) { case "supply": userMessage = `You don't have enough ${asset} to supply this amount.`; break; case "borrow": userMessage = `You don't have enough collateral to borrow this amount of ${asset}.`; break; case "repay": userMessage = `You don't have enough ${asset} to repay this amount.`; break; case "withdraw": userMessage = `You don't have enough supplied ${asset} to withdraw this amount.`; break; } } return { originalError: error, aaveError, userMessage, technicalMessage, context: enhancedContext, isRetryable: determineRetryability(aaveError), logLevel: determineLogLevel(aaveError), recovery: generateRecoverySuggestion(aaveError, context) }; } function handleBigNumberError(error, context = {}) { const errorMessage = error.message.toLowerCase(); let aaveError; let userMessage; if (errorMessage.includes("invalid number") || errorMessage.includes("not a number")) { aaveError = new AaveError( "Invalid number format provided.", "INVALID_PARAMETERS" /* INVALID_PARAMETERS */, error, context ); userMessage = "Please enter a valid number."; } else if (errorMessage.includes("overflow") || errorMessage.includes("too large")) { aaveError = new AaveError( "Number is too large to process.", "AMOUNT_TOO_HIGH" /* AMOUNT_TOO_HIGH */, error, context ); userMessage = "The amount is too large. Please use a smaller number."; } else if (errorMessage.includes("underflow") || errorMessage.includes("negative")) { aaveError = new AaveError( "Negative numbers are not allowed.", "INVALID_PARAMETERS" /* INVALID_PARAMETERS */, error, context ); userMessage = "Please enter a positive number."; } else if (errorMessage.includes("division by zero")) { aaveError = new AaveError( "Division by zero error in calculation.", "TRANSACTION_FAILED" /* TRANSACTION_FAILED */, error, context ); userMessage = "Internal calculation error. Please try again."; } else { aaveError = new AaveError( "Number processing error.", "INVALID_PARAMETERS" /* INVALID_PARAMETERS */, error, context ); userMessage = "There was an error processing the numbers. Please check your input."; } return { originalError: error, aaveError, userMessage, technicalMessage: `BigNumber/ethers error: ${error.message}`, context, isRetryable: false, logLevel: "warn", recovery: { action: "Check input values and try again", requiresUserAction: true } }; } function createSupplyErrorContext(params) { return { operation: "supply", asset: params.asset, amount: params.amount?.toString(), user: params.user }; } function createWithdrawErrorContext(params) { return { operation: "withdraw", asset: params.asset, amount: params.amount?.toString(), user: params.user }; } function createBorrowErrorContext(params) { return { operation: "borrow", asset: params.asset, amount: params.amount?.toString(), user: params.user, interestRateMode: params.interestRateMode }; } function createRepayErrorContext(params) { return { operation: "repay", asset: params.asset, amount: params.amount?.toString(), user: params.user, interestRateMode: params.interestRateMode }; } function determineRetryability(error) { const retryableCodes = [ "UNKNOWN" /* UNKNOWN */, "TRANSACTION_FAILED" /* TRANSACTION_FAILED */ ]; if (retryableCodes.includes(error.code)) { if (error.context?.isRetryable !== void 0) { return error.context.isRetryable; } return true; } const nonRetryableCodes = [ "INSUFFICIENT_BALANCE" /* INSUFFICIENT_BALANCE */, "INSUFFICIENT_COLLATERAL" /* INSUFFICIENT_COLLATERAL */, "ASSET_NOT_SUPPORTED" /* ASSET_NOT_SUPPORTED */, "INVALID_PARAMETERS" /* INVALID_PARAMETERS */, "HEALTH_FACTOR_TOO_LOW" /* HEALTH_FACTOR_TOO_LOW */, "STABLE_BORROWING_NOT_ENABLED" /* STABLE_BORROWING_NOT_ENABLED */, "AMOUNT_TOO_HIGH" /* AMOUNT_TOO_HIGH */ ]; return !nonRetryableCodes.includes(error.code); } function determineLogLevel(error) { switch (error.code) { case "INVALID_PARAMETERS" /* INVALID_PARAMETERS */: case "ASSET_NOT_SUPPORTED" /* ASSET_NOT_SUPPORTED */: return "warn"; case "INSUFFICIENT_BALANCE" /* INSUFFICIENT_BALANCE */: case "INSUFFICIENT_COLLATERAL" /* INSUFFICIENT_COLLATERAL */: case "HEALTH_FACTOR_TOO_LOW" /* HEALTH_FACTOR_TOO_LOW */: return "info"; default: return "error"; } } function generateRecoverySuggestion(error, context) { if (error.context?.recovery) { return error.context.recovery; } const { operation, asset, interestRateMode } = context; switch (error.code) { case "INSUFFICIENT_BALANCE" /* INSUFFICIENT_BALANCE */: if (operation === "supply" || operation === "repay") { return { action: `Acquire more ${asset || "tokens"} or reduce the amount`, requiresUserAction: true }; } return { action: "Check your wallet balance and try a smaller amount", requiresUserAction: true }; case "INSUFFICIENT_COLLATERAL" /* INSUFFICIENT_COLLATERAL */: return { action: "Supply more collateral or reduce the borrow amount", details: "Your collateral is not sufficient for this borrow amount", requiresUserAction: true }; case "HEALTH_FACTOR_TOO_LOW" /* HEALTH_FACTOR_TOO_LOW */: return { action: "Supply additional collateral or repay some debt first", details: "This operation would put your position at risk of liquidation", requiresUserAction: true }; case "STABLE_BORROWING_NOT_ENABLED" /* STABLE_BORROWING_NOT_ENABLED */: return { action: "Use variable rate borrowing instead", details: `Stable rate is not available for ${asset || "this asset"}`, requiresUserAction: true }; case "ASSET_NOT_SUPPORTED" /* ASSET_NOT_SUPPORTED */: return { action: "Choose a supported asset from the Aave markets", requiresUserAction: true }; case "RESERVE_FROZEN" /* RESERVE_FROZEN */: return { action: "Only withdraw and repay operations are allowed for frozen assets", requiresUserAction: true }; case "RESERVE_INACTIVE" /* RESERVE_INACTIVE */: return { action: "Choose an active asset or wait for this asset to be reactivated", requiresUserAction: true }; case "POOL_PAUSED" /* POOL_PAUSED */: return { action: "Wait for the pool to be unpaused and try again", requiresUserAction: false }; default: return { action: "Review the error details and try again", requiresUserAction: true }; } } function formatErrorForLogging(processedError) { return { timestamp: (/* @__PURE__ */ new Date()).toISOString(), level: processedError.logLevel, error: { type: processedError.aaveError.name, code: processedError.aaveError.code, message: processedError.technicalMessage, userMessage: processedError.userMessage, stack: processedError.originalError.stack }, context: processedError.context, recovery: processedError.recovery, isRetryable: processedError.isRetryable }; } function logError(processedError, logger) { const logData = formatErrorForLogging(processedError); { const logPrefix = `[${processedError.logLevel.toUpperCase()}] Aave Error:`; console[processedError.logLevel === "error" ? "error" : "log"]( logPrefix, JSON.stringify(logData, null, 2) ); } } function processError(error, operation, params, transactionHash) { let context; switch (operation) { case "supply": context = createSupplyErrorContext(params || {}); break; case "withdraw": context = createWithdrawErrorContext(params || {}); break; case "borrow": context = createBorrowErrorContext(params || {}); break; case "repay": context = createRepayErrorContext(params || {}); break; default: context = { operation }; } if (error.message.includes("BigNumber") || error.message.includes("invalid number")) { return handleBigNumberError(error, context); } if (error.message.includes("transaction")) { return handleTransactionError(error, transactionHash, context); } return handleContractError(error, "AavePool", operation, context); } function createUserFriendlyMessage(error) { const { aaveError, context } = error; const { operation, asset, amount, interestRateMode } = context; let baseMessage = aaveError.message; let operationContext = ""; switch (operation) { case "supply": operationContext = `while supplying ${amount ? `${amount} ` : ""}${asset || "tokens"}`; break; case "withdraw": operationContext = `while withdrawing ${amount ? `${amount} ` : ""}${asset || "tokens"}`; break; case "borrow": const rateType = interestRateMode ? getRateModeName(interestRateMode).toLowerCase() : ""; operationContext = `while borrowing ${amount ? `${amount} ` : ""}${asset || "tokens"}${rateType ? ` at ${rateType} rate` : ""}`; break; case "repay": const repayRateType = interestRateMode ? getRateModeName(interestRateMode).toLowerCase() : ""; operationContext = `while repaying ${amount ? `${amount} ` : ""}${asset || "debt"}${repayRateType ? ` (${repayRateType} rate)` : ""}`; break; } if (operationContext) { baseMessage = `${baseMessage} This error occurred ${operationContext}.`; } if (error.recovery) { baseMessage += ` ${error.recovery.action}.`; if (error.recovery.details) { baseMessage += ` ${error.recovery.details}.`; } } return baseMessage; } function handleError(error, operation, params, transactionHash, logger) { const processedError = processError(error, operation, params, transactionHash); processedError.userMessage = createUserFriendlyMessage(processedError); logError(processedError); return processedError; } // src/services/aave-service.ts var AaveService = class extends Service { static serviceType = "aave"; provider; signer; poolService; uiPoolDataProvider; chainContext; isInitialized = false; constructor(runtime) { super(runtime); } get description() { return "Aave V3 Protocol service for lending, borrowing, and DeFi operations"; } get capabilityDescription() { const currentChain = this.chainContext?.config?.name || "Multiple chains"; return `Supports Aave V3 lending operations on ${currentChain}: supply, withdraw, borrow, repay with aTokens and variable/stable rates`; } async initialize(runtime) { try { elizaLogger.info("Initializing Aave V3 multi-chain service..."); const targetChain = runtime.getSetting("AAVE_CHAIN") || "ethereum"; elizaLogger.info(`Initializing for chain: ${targetChain}`); this.chainContext = resolveChainContext(targetChain); elizaLogger.info(`Chain context resolved: ${this.chainContext.config.name} (Chain ID: ${this.chainContext.config.chainId})`); const customRpcUrl = runtime.getSetting("AAVE_RPC_URL") || runtime.getSetting("RPC_URL"); const rpcUrl = resolveRpcUrl(this.chainContext, customRpcUrl); elizaLogger.info(`Using RPC URL: ${rpcUrl.replace(/\/\/.*@/, "//***@")}`); const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY"); this.provider = new ethers.providers.JsonRpcProvider(rpcUrl); if (privateKey) { this.signer = new ethers.Wallet(privateKey, this.provider); elizaLogger.info(`Wallet connected: ${this.signer.address} on ${this.chainContext.config.name}`); const network = await this.provider.getNetwork(); if (network.chainId !== this.chainContext.config.chainId) { elizaLogger.warn(`Network mismatch: RPC reports chain ID ${network.chainId}, expected ${this.chainContext.config.chainId}`); } } const poolConfig = { POOL: this.chainContext.addresses.POOL }; if (this.chainContext.addresses.WETH_GATEWAY) { poolConfig.WETH_GATEWAY = this.chainContext.addresses.WETH_GATEWAY; } this.poolService = new Pool(this.provider, poolConfig); if (this.chainContext.addresses.UI_POOL_DATA_PROVIDER) { this.uiPoolDataProvider = new UiPoolDataProvider({ uiPoolDataProviderAddress: this.chainContext.addresses.UI_POOL_DATA_PROVIDER, provider: this.provider, chainId: this.chainContext.config.chainId }); } await this.verifyConnection(); this.isInitialized = true; elizaLogger.info(`Aave V3 service initialized successfully on ${this.chainContext.config.name}`); elizaLogger.info(`Available assets: ${this.chainContext.config.popularAssets.join(", ")}`); } catch (error) {