use alloy_primitives::{address, Address};
use alloy_provider::{network::Ethereum, Provider};
use anyhow::Result;
use wp_evm_aave_v3_provider as aave;
use wp_evm_base::chain::Chain;
pub use wp_evm_aave_v3_provider::data::{
AaveV3MarketConfig, ReserveState, SupplyParams, UserAccountData, WithdrawParams,
};
const MULTICALL3: Address = address!("cA11bde05977b3631167028862bE2a173976CA11");
pub const CONFIG: AaveV3MarketConfig = AaveV3MarketConfig {
addresses_provider: address!("2f39d218133AFaB8F2B819B1066c7E434Ad94E9e"),
pool: address!("87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2"),
protocol_data_provider: address!("0a16f2FCC0D44FaE41cc54e079281D84A363bECD"),
multicall: MULTICALL3,
};
const SHARED_L2: AaveV3MarketConfig = AaveV3MarketConfig {
addresses_provider: address!("a97684ead0e402dC232d5A977953DF7ECBaB3CDb"),
pool: address!("794a61358D6845594F94dc1DB02A252b5b4814aD"),
protocol_data_provider: address!("243Aa95cAC2a25651eda86e80bEe66114413c43b"),
multicall: MULTICALL3,
};
pub const CONFIG_ARBITRUM: AaveV3MarketConfig = SHARED_L2;
pub const CONFIG_OPTIMISM: AaveV3MarketConfig = SHARED_L2;
pub const CONFIG_POLYGON: AaveV3MarketConfig = SHARED_L2;
pub const CONFIG_AVALANCHE: AaveV3MarketConfig = SHARED_L2;
pub const CONFIG_BASE: AaveV3MarketConfig = AaveV3MarketConfig {
addresses_provider: address!("e20fCBdBfFC4Dd138cE8b2E6FBb6CB49777ad64D"),
pool: address!("A238Dd80C259a72e81d7e4664a9801593F98d1c5"),
protocol_data_provider: address!("0F43731EB8d45A581f4a36DD74F5f358bc90C73A"),
multicall: MULTICALL3,
};
pub fn config_for_chain(chain: Chain) -> Option<&'static AaveV3MarketConfig> {
match chain {
Chain::Ethereum => Some(&CONFIG),
Chain::Arbitrum => Some(&CONFIG_ARBITRUM),
Chain::Optimism => Some(&CONFIG_OPTIMISM),
Chain::Polygon => Some(&CONFIG_POLYGON),
Chain::Avalanche => Some(&CONFIG_AVALANCHE),
Chain::Base => Some(&CONFIG_BASE),
Chain::Bsc | Chain::Sonic | Chain::Celo | Chain::HyperEvm => None,
}
}
pub async fn reserve_state<P: Provider<Ethereum>>(
provider: &P,
chain: Chain,
asset: Address,
) -> Result<ReserveState> {
let cfg = config_for_chain(chain)
.ok_or_else(|| anyhow::anyhow!("no Aave v3 config for chain {}", chain.name()))?;
aave::hydrate::reserve_state(provider, cfg.multicall, cfg.pool, asset).await
}
pub async fn user_account_data<P: Provider<Ethereum>>(
provider: &P,
chain: Chain,
user: Address,
) -> Result<UserAccountData> {
let cfg = config_for_chain(chain)
.ok_or_else(|| anyhow::anyhow!("no Aave v3 config for chain {}", chain.name()))?;
aave::hydrate::user_account_data(provider, cfg.multicall, cfg.pool, user).await
}
pub fn plan_supply(
chain: Chain,
params: &SupplyParams,
) -> Result<wp_evm_aave_v3_provider::data::PlanFragment> {
let cfg = config_for_chain(chain)
.ok_or_else(|| anyhow::anyhow!("no Aave v3 config for chain {}", chain.name()))?;
Ok(wp_evm_aave_v3_provider::plan::plan_supply(params, cfg.pool))
}
pub fn plan_withdraw(
chain: Chain,
params: &WithdrawParams,
) -> Result<wp_evm_aave_v3_provider::data::PlanFragment> {
let cfg = config_for_chain(chain)
.ok_or_else(|| anyhow::anyhow!("no Aave v3 config for chain {}", chain.name()))?;
Ok(wp_evm_aave_v3_provider::plan::plan_withdraw(params, cfg.pool))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ethereum_config_addresses_are_set() {
let cfg = config_for_chain(Chain::Ethereum).expect("ethereum config");
assert_eq!(cfg.pool, address!("87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2"));
assert_eq!(cfg.multicall, MULTICALL3);
}
#[test]
fn shared_l2_chains_use_canonical_pool() {
for chain in [Chain::Arbitrum, Chain::Optimism, Chain::Polygon, Chain::Avalanche] {
let cfg = config_for_chain(chain).unwrap_or_else(|| panic!("config for {chain}"));
assert_eq!(
cfg.pool,
address!("794a61358D6845594F94dc1DB02A252b5b4814aD"),
"wrong pool for {chain}"
);
assert_eq!(
cfg.addresses_provider,
address!("a97684ead0e402dC232d5A977953DF7ECBaB3CDb"),
"wrong addresses_provider for {chain}"
);
assert_eq!(cfg.multicall, MULTICALL3, "wrong multicall for {chain}");
}
}
#[test]
fn base_has_its_own_deployment() {
let cfg = config_for_chain(Chain::Base).expect("base config");
assert_eq!(cfg.pool, address!("A238Dd80C259a72e81d7e4664a9801593F98d1c5"));
assert_eq!(cfg.addresses_provider, address!("e20fCBdBfFC4Dd138cE8b2E6FBb6CB49777ad64D"));
assert_eq!(cfg.multicall, MULTICALL3);
}
#[test]
fn chains_without_aave_v3_return_none() {
for chain in [Chain::Bsc, Chain::Sonic, Chain::Celo, Chain::HyperEvm] {
assert!(config_for_chain(chain).is_none(), "{chain} must be None");
}
}
#[test]
fn supply_plan_uses_ethereum_pool() {
use alloy_primitives::{address, U256};
let f = plan_supply(
Chain::Ethereum,
&SupplyParams {
asset: address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
amount: U256::from(100u64),
on_behalf_of: address!("000000000000000000000000000000000000dEaD"),
},
)
.expect("ethereum supply plan");
assert_eq!(f.calls[0].target, CONFIG.pool);
assert_eq!(f.approvals[0].spender, CONFIG.pool);
}
#[test]
fn supply_plan_unsupported_chain_errors() {
use alloy_primitives::{address, U256};
let err = plan_supply(
Chain::Bsc,
&SupplyParams {
asset: address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
amount: U256::from(1u64),
on_behalf_of: address!("000000000000000000000000000000000000dEaD"),
},
)
.unwrap_err();
assert!(format!("{err:#}").contains("no Aave v3 config for chain bsc"));
}
}