@elizaos/plugin-aave
Version:
ElizaOS plugin for Aave Protocol - DeFi lending and borrowing
1,383 lines (1,377 loc) • 112 kB
JavaScript
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) {