Skip to content

Commit c9eec50

Browse files
Hiksangclaude
andcommitted
Add Compound V3 (Arb+Base), TraderJoe — 55 verified protocols
- Compound V3 Arbitrum: supply=1.77%, borrow=3.02%, util=45.4% - Compound V3 Base: supply=2.83%, borrow=3.68%, util=78.5% - TraderJoe V2.1 Arbitrum: swap simulation verified - New compound_v3 adapter (Comet interface) HyperEVM: 22 | Arbitrum: 9 | Base: 10 | BNB: 14 = 55 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2412650 commit c9eec50

7 files changed

Lines changed: 226 additions & 1 deletion

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[protocol]
2+
name = "TraderJoe V2.1"
3+
slug = "traderjoe-arb"
4+
category = "dex"
5+
interface = "uniswap_v2"
6+
chain = "arbitrum"
7+
native = false
8+
description = "TraderJoe V2.1 on Arbitrum ($1.3M TVL)"
9+
10+
[protocol.contracts]
11+
router = "0xb4315e873dBcf96Ffd0acd8EA43f689D8c20fB30"
12+
factory = "0x8e42f2F4101563bF679975178e880FD87d3eFd4e"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[protocol]
2+
name = "Compound V3 Arbitrum"
3+
slug = "compound-v3-arb"
4+
category = "lending"
5+
interface = "compound_v3"
6+
chain = "arbitrum"
7+
native = false
8+
description = "Compound V3 (Comet) on Arbitrum ($87M TVL)"
9+
10+
[protocol.contracts]
11+
comet_usdc = "0xA5EDBDD9646f8dFF606d7448e414884C7d905dCA"
12+
comet_weth = "0x6f7D514bbD4aFf3BcD1140B7344b32f063dEe486"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[protocol]
2+
name = "Compound V3 Base"
3+
slug = "compound-v3-base"
4+
category = "lending"
5+
interface = "compound_v3"
6+
chain = "base"
7+
native = false
8+
description = "Compound V3 (Comet) on Base ($30M TVL)"
9+
10+
[protocol.contracts]
11+
comet_usdc = "0xb125E6687d4313864e53df431d5425969c15Eb2F"
12+
comet_weth = "0x46e6b214b524310239732D51387075E0e70970bf"

crates/defi-core/src/registry/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,18 @@ impl Registry {
179179
"dex/pancakeswap_v3_arb",
180180
include_str!("../../../../config/protocols/dex/pancakeswap_v3_arb.toml"),
181181
),
182+
(
183+
"dex/traderjoe_arb",
184+
include_str!("../../../../config/protocols/dex/traderjoe_arb.toml"),
185+
),
182186
(
183187
"lending/aave_v3_arb",
184188
include_str!("../../../../config/protocols/lending/aave_v3_arb.toml"),
185189
),
190+
(
191+
"lending/compound_v3_arb",
192+
include_str!("../../../../config/protocols/lending/compound_v3_arb.toml"),
193+
),
186194
(
187195
"dex/camelot_v2_arb",
188196
include_str!("../../../../config/protocols/dex/camelot_v2_arb.toml"),
@@ -224,6 +232,10 @@ impl Registry {
224232
"lending/aave_v3_base",
225233
include_str!("../../../../config/protocols/lending/aave_v3_base.toml"),
226234
),
235+
(
236+
"lending/compound_v3_base",
237+
include_str!("../../../../config/protocols/lending/compound_v3_base.toml"),
238+
),
227239
// === BNB ===
228240
(
229241
"dex/pancakeswap_v3",

crates/defi-protocols/src/factory.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::derivatives::{GenericDerivatives, HlpVault};
99
use crate::dex::{
1010
AlgebraV3, BalancerV3, CurveStableSwap, Solidly, SolidlyGauge, UniswapV2, UniswapV3, WooFi,
1111
};
12-
use crate::lending::{AaveOracle, AaveV3, CompoundV2, EulerV2, MorphoBlue};
12+
use crate::lending::{AaveOracle, AaveV3, CompoundV2, CompoundV3, EulerV2, MorphoBlue};
1313
use crate::liquid_staking::{GenericLst, Kinetiq, StHype};
1414
use crate::options::{GenericOptions, Rysk};
1515
use crate::vault::Erc4626Vault;
@@ -90,6 +90,11 @@ pub fn create_lending_with_rpc(
9090
&entry.contracts,
9191
rpc_url.map(|s| s.to_string()),
9292
)?)),
93+
"compound_v3" => Ok(Box::new(CompoundV3::from_contracts(
94+
entry.name.clone(),
95+
&entry.contracts,
96+
rpc_url.map(|s| s.to_string()),
97+
)?)),
9398
other => Err(DefiError::Unsupported(format!(
9499
"Lending interface '{other}' not yet implemented"
95100
))),
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
use alloy::primitives::{Address, U256};
2+
use alloy::providers::ProviderBuilder;
3+
use alloy::sol;
4+
use alloy::sol_types::SolCall;
5+
use async_trait::async_trait;
6+
7+
use defi_core::error::{DefiError, Result};
8+
use defi_core::traits::Lending;
9+
use defi_core::types::*;
10+
11+
sol! {
12+
#[sol(rpc)]
13+
interface IComet {
14+
function getUtilization() external view returns (uint256);
15+
function getSupplyRate(uint256 utilization) external view returns (uint64);
16+
function getBorrowRate(uint256 utilization) external view returns (uint64);
17+
function totalSupply() external view returns (uint256);
18+
function totalBorrow() external view returns (uint256);
19+
function supply(address asset, uint256 amount) external;
20+
function withdraw(address asset, uint256 amount) external;
21+
}
22+
}
23+
24+
/// Seconds per year for rate conversion
25+
const SECONDS_PER_YEAR: f64 = 365.25 * 24.0 * 3600.0;
26+
27+
pub struct CompoundV3 {
28+
name: String,
29+
comet: Address,
30+
rpc_url: Option<String>,
31+
}
32+
33+
impl CompoundV3 {
34+
pub fn from_contracts(
35+
name: String,
36+
contracts: &std::collections::HashMap<String, Address>,
37+
rpc_url: Option<String>,
38+
) -> Result<Self> {
39+
let comet = contracts
40+
.get("comet_usdc")
41+
.or(contracts.get("comet"))
42+
.or(contracts.get("comet_weth"))
43+
.copied()
44+
.ok_or_else(|| {
45+
DefiError::ContractError("Missing 'comet_usdc' or 'comet' address".to_string())
46+
})?;
47+
Ok(Self {
48+
name,
49+
comet,
50+
rpc_url,
51+
})
52+
}
53+
54+
fn rpc_url(&self) -> Result<url::Url> {
55+
self.rpc_url
56+
.as_ref()
57+
.ok_or_else(|| DefiError::RpcError("No RPC URL configured".to_string()))?
58+
.parse()
59+
.map_err(|e| DefiError::RpcError(format!("Invalid RPC URL: {e}")))
60+
}
61+
}
62+
63+
#[async_trait]
64+
impl Lending for CompoundV3 {
65+
fn name(&self) -> &str {
66+
&self.name
67+
}
68+
69+
async fn build_supply(&self, params: SupplyParams) -> Result<DeFiTx> {
70+
let call = IComet::supplyCall {
71+
asset: params.asset,
72+
amount: params.amount,
73+
};
74+
Ok(DeFiTx {
75+
description: format!("[{}] Supply {} to Comet", self.name, params.amount),
76+
to: self.comet,
77+
data: call.abi_encode().into(),
78+
value: U256::ZERO,
79+
gas_estimate: Some(300_000),
80+
})
81+
}
82+
83+
async fn build_borrow(&self, params: BorrowParams) -> Result<DeFiTx> {
84+
let call = IComet::withdrawCall {
85+
asset: params.asset,
86+
amount: params.amount,
87+
};
88+
Ok(DeFiTx {
89+
description: format!("[{}] Borrow {} from Comet", self.name, params.amount),
90+
to: self.comet,
91+
data: call.abi_encode().into(),
92+
value: U256::ZERO,
93+
gas_estimate: Some(350_000),
94+
})
95+
}
96+
97+
async fn build_repay(&self, params: RepayParams) -> Result<DeFiTx> {
98+
let call = IComet::supplyCall {
99+
asset: params.asset,
100+
amount: params.amount,
101+
};
102+
Ok(DeFiTx {
103+
description: format!("[{}] Repay {} to Comet", self.name, params.amount),
104+
to: self.comet,
105+
data: call.abi_encode().into(),
106+
value: U256::ZERO,
107+
gas_estimate: Some(300_000),
108+
})
109+
}
110+
111+
async fn build_withdraw(&self, params: WithdrawParams) -> Result<DeFiTx> {
112+
let call = IComet::withdrawCall {
113+
asset: params.asset,
114+
amount: params.amount,
115+
};
116+
Ok(DeFiTx {
117+
description: format!("[{}] Withdraw from Comet", self.name),
118+
to: self.comet,
119+
data: call.abi_encode().into(),
120+
value: U256::ZERO,
121+
gas_estimate: Some(250_000),
122+
})
123+
}
124+
125+
async fn get_rates(&self, _asset: Address) -> Result<LendingRates> {
126+
let url = self.rpc_url()?;
127+
let provider = ProviderBuilder::new().connect_http(url);
128+
let comet = IComet::new(self.comet, &provider);
129+
130+
let utilization = comet.getUtilization().call().await.map_err(|e| {
131+
DefiError::RpcError(format!("[{}] getUtilization failed: {e}", self.name))
132+
})?;
133+
134+
let supply_rate = comet.getSupplyRate(utilization).call().await.map_err(|e| {
135+
DefiError::RpcError(format!("[{}] getSupplyRate failed: {e}", self.name))
136+
})?;
137+
138+
let borrow_rate = comet.getBorrowRate(utilization).call().await.map_err(|e| {
139+
DefiError::RpcError(format!("[{}] getBorrowRate failed: {e}", self.name))
140+
})?;
141+
142+
let total_supply = comet.totalSupply().call().await.unwrap_or(U256::ZERO);
143+
let total_borrow = comet.totalBorrow().call().await.unwrap_or(U256::ZERO);
144+
145+
// Comet rates are per-second, scaled by 1e18
146+
let supply_per_sec = supply_rate as f64 / 1e18;
147+
let borrow_per_sec = borrow_rate as f64 / 1e18;
148+
let supply_apy = supply_per_sec * SECONDS_PER_YEAR * 100.0;
149+
let borrow_apy = borrow_per_sec * SECONDS_PER_YEAR * 100.0;
150+
let util_pct = utilization.to::<u128>() as f64 / 1e18 * 100.0;
151+
152+
Ok(LendingRates {
153+
protocol: self.name.clone(),
154+
asset: _asset,
155+
supply_apy,
156+
borrow_variable_apy: borrow_apy,
157+
borrow_stable_apy: None,
158+
utilization: util_pct,
159+
total_supply,
160+
total_borrow,
161+
})
162+
}
163+
164+
async fn get_user_position(&self, _user: Address) -> Result<UserPosition> {
165+
Err(DefiError::Unsupported(format!(
166+
"[{}] User position requires querying Comet balanceOf + borrowBalanceOf",
167+
self.name
168+
)))
169+
}
170+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
pub mod aave_oracle;
22
pub mod aave_v3;
33
pub mod compound_v2;
4+
pub mod compound_v3;
45
pub mod euler_v2;
56
pub mod morpho;
67

78
pub use aave_oracle::AaveOracle;
89
pub use aave_v3::AaveV3;
910
pub use compound_v2::CompoundV2;
11+
pub use compound_v3::CompoundV3;
1012
pub use euler_v2::EulerV2;
1113
pub use morpho::MorphoBlue;

0 commit comments

Comments
 (0)