1use alloy_primitives::{address, Address};
8use alloy_provider::{network::Ethereum, Provider};
9use anyhow::Result;
10use wp_evm_aave_v3_provider as aave;
11use wp_evm_base::chain::Chain;
12
13pub use wp_evm_aave_v3_provider::data::{
18 AaveV3MarketConfig, ReserveState, SupplyParams, UserAccountData, WithdrawParams,
19};
20
21const MULTICALL3: Address = address!("cA11bde05977b3631167028862bE2a173976CA11");
22
23pub const CONFIG: AaveV3MarketConfig = AaveV3MarketConfig {
28 addresses_provider: address!("2f39d218133AFaB8F2B819B1066c7E434Ad94E9e"),
29 pool: address!("87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2"),
30 protocol_data_provider: address!("0a16f2FCC0D44FaE41cc54e079281D84A363bECD"),
31 multicall: MULTICALL3,
32};
33
34const SHARED_L2: AaveV3MarketConfig = AaveV3MarketConfig {
38 addresses_provider: address!("a97684ead0e402dC232d5A977953DF7ECBaB3CDb"),
39 pool: address!("794a61358D6845594F94dc1DB02A252b5b4814aD"),
40 protocol_data_provider: address!("243Aa95cAC2a25651eda86e80bEe66114413c43b"),
41 multicall: MULTICALL3,
42};
43
44pub const CONFIG_ARBITRUM: AaveV3MarketConfig = SHARED_L2;
46pub const CONFIG_OPTIMISM: AaveV3MarketConfig = SHARED_L2;
48pub const CONFIG_POLYGON: AaveV3MarketConfig = SHARED_L2;
50pub const CONFIG_AVALANCHE: AaveV3MarketConfig = SHARED_L2;
52
53pub const CONFIG_BASE: AaveV3MarketConfig = AaveV3MarketConfig {
56 addresses_provider: address!("e20fCBdBfFC4Dd138cE8b2E6FBb6CB49777ad64D"),
57 pool: address!("A238Dd80C259a72e81d7e4664a9801593F98d1c5"),
58 protocol_data_provider: address!("0F43731EB8d45A581f4a36DD74F5f358bc90C73A"),
59 multicall: MULTICALL3,
60};
61
62pub fn config_for_chain(chain: Chain) -> Option<&'static AaveV3MarketConfig> {
67 match chain {
68 Chain::Ethereum => Some(&CONFIG),
69 Chain::Arbitrum => Some(&CONFIG_ARBITRUM),
70 Chain::Optimism => Some(&CONFIG_OPTIMISM),
71 Chain::Polygon => Some(&CONFIG_POLYGON),
72 Chain::Avalanche => Some(&CONFIG_AVALANCHE),
73 Chain::Base => Some(&CONFIG_BASE),
74 Chain::Bsc | Chain::Sonic | Chain::Celo | Chain::HyperEvm => None,
75 }
76}
77
78pub async fn reserve_state<P: Provider<Ethereum>>(
81 provider: &P,
82 chain: Chain,
83 asset: Address,
84) -> Result<ReserveState> {
85 let cfg = config_for_chain(chain)
86 .ok_or_else(|| anyhow::anyhow!("no Aave v3 config for chain {}", chain.name()))?;
87 aave::hydrate::reserve_state(provider, cfg.multicall, cfg.pool, asset).await
88}
89
90pub async fn user_account_data<P: Provider<Ethereum>>(
92 provider: &P,
93 chain: Chain,
94 user: Address,
95) -> Result<UserAccountData> {
96 let cfg = config_for_chain(chain)
97 .ok_or_else(|| anyhow::anyhow!("no Aave v3 config for chain {}", chain.name()))?;
98 aave::hydrate::user_account_data(provider, cfg.multicall, cfg.pool, user).await
99}
100
101pub fn plan_supply(
103 chain: Chain,
104 params: &SupplyParams,
105) -> Result<wp_evm_aave_v3_provider::data::PlanFragment> {
106 let cfg = config_for_chain(chain)
107 .ok_or_else(|| anyhow::anyhow!("no Aave v3 config for chain {}", chain.name()))?;
108 Ok(wp_evm_aave_v3_provider::plan::plan_supply(params, cfg.pool))
109}
110
111pub fn plan_withdraw(
113 chain: Chain,
114 params: &WithdrawParams,
115) -> Result<wp_evm_aave_v3_provider::data::PlanFragment> {
116 let cfg = config_for_chain(chain)
117 .ok_or_else(|| anyhow::anyhow!("no Aave v3 config for chain {}", chain.name()))?;
118 Ok(wp_evm_aave_v3_provider::plan::plan_withdraw(params, cfg.pool))
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn ethereum_config_addresses_are_set() {
127 let cfg = config_for_chain(Chain::Ethereum).expect("ethereum config");
128 assert_eq!(cfg.pool, address!("87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2"));
129 assert_eq!(cfg.multicall, MULTICALL3);
130 }
131
132 #[test]
133 fn shared_l2_chains_use_canonical_pool() {
134 for chain in [Chain::Arbitrum, Chain::Optimism, Chain::Polygon, Chain::Avalanche] {
137 let cfg = config_for_chain(chain).unwrap_or_else(|| panic!("config for {chain}"));
138 assert_eq!(
139 cfg.pool,
140 address!("794a61358D6845594F94dc1DB02A252b5b4814aD"),
141 "wrong pool for {chain}"
142 );
143 assert_eq!(
144 cfg.addresses_provider,
145 address!("a97684ead0e402dC232d5A977953DF7ECBaB3CDb"),
146 "wrong addresses_provider for {chain}"
147 );
148 assert_eq!(cfg.multicall, MULTICALL3, "wrong multicall for {chain}");
149 }
150 }
151
152 #[test]
153 fn base_has_its_own_deployment() {
154 let cfg = config_for_chain(Chain::Base).expect("base config");
155 assert_eq!(cfg.pool, address!("A238Dd80C259a72e81d7e4664a9801593F98d1c5"));
156 assert_eq!(cfg.addresses_provider, address!("e20fCBdBfFC4Dd138cE8b2E6FBb6CB49777ad64D"));
157 assert_eq!(cfg.multicall, MULTICALL3);
158 }
159
160 #[test]
161 fn chains_without_aave_v3_return_none() {
162 for chain in [Chain::Bsc, Chain::Sonic, Chain::Celo, Chain::HyperEvm] {
164 assert!(config_for_chain(chain).is_none(), "{chain} must be None");
165 }
166 }
167
168 #[test]
169 fn supply_plan_uses_ethereum_pool() {
170 use alloy_primitives::{address, U256};
171 let f = plan_supply(
172 Chain::Ethereum,
173 &SupplyParams {
174 asset: address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
175 amount: U256::from(100u64),
176 on_behalf_of: address!("000000000000000000000000000000000000dEaD"),
177 },
178 )
179 .expect("ethereum supply plan");
180 assert_eq!(f.calls[0].target, CONFIG.pool);
181 assert_eq!(f.approvals[0].spender, CONFIG.pool);
182 }
183
184 #[test]
185 fn supply_plan_unsupported_chain_errors() {
186 use alloy_primitives::{address, U256};
187 let err = plan_supply(
188 Chain::Bsc,
189 &SupplyParams {
190 asset: address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
191 amount: U256::from(1u64),
192 on_behalf_of: address!("000000000000000000000000000000000000dEaD"),
193 },
194 )
195 .unwrap_err();
196 assert!(format!("{err:#}").contains("no Aave v3 config for chain bsc"));
197 }
198}