Skip to content

Commit 8961878

Browse files
authored
Merge pull request #9 from xdecentralix/reclamm
Reclamm
2 parents 5adf731 + c33a414 commit 8961878

File tree

37 files changed

+1783
-21
lines changed

37 files changed

+1783
-21
lines changed

crates/contracts/artifacts/BalancerV3ReClammPool.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

crates/contracts/artifacts/BalancerV3ReClammPoolFactoryV2.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

crates/contracts/build.rs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1324,10 +1324,69 @@ fn main() {
13241324
},
13251325
)
13261326
});
1327-
1327+
generate_contract_with_config("BalancerV3ReClammPoolFactoryV2", |builder| {
1328+
builder
1329+
.add_network(
1330+
ARBITRUM_ONE,
1331+
Network {
1332+
address: addr("0x355bD33F0033066BB3DE396a6d069be57353AD95"),
1333+
// <https://arbiscan.io/tx/0xb544a2bdea93f632fd739df575cc67bbb6d55e969b585fc93ba49b6a22bb5912>
1334+
deployment_information: Some(DeploymentInformation::BlockNumber(353502388)),
1335+
},
1336+
)
1337+
.add_network(
1338+
AVALANCHE,
1339+
Network {
1340+
address: addr("0x309abcAeFa19CA6d34f0D8ff4a4103317c138657"),
1341+
// <https://snowscan.xyz/tx/0x5d85462e695ff43bc6a3624c5a47ed7cd4e1057373c6ea7052e3a1c7cd16fc21>
1342+
deployment_information: Some(DeploymentInformation::BlockNumber(64832650)),
1343+
},
1344+
)
1345+
.add_network(
1346+
BASE,
1347+
Network {
1348+
address: addr("0x201efd508c8DfE9DE1a13c2452863A78CB2a86Cc"),
1349+
// <https://basescan.org/tx/0x1c3574deb31beba51d3b1cb5e45fede50cdf497793b54926f59ef883a2877f68>
1350+
deployment_information: Some(DeploymentInformation::BlockNumber(32339174)),
1351+
},
1352+
)
1353+
.add_network(
1354+
GNOSIS,
1355+
Network {
1356+
address: addr("0xc86eF81E57492BE65BFCa9b0Ed53dCBAfDBe6100"),
1357+
// <https://gnosisscan.io/tx/0x61a5e5571a5e20ca3819abb7952f26194496cb1b87fcc9c6d36e4a03c663d704>
1358+
deployment_information: Some(DeploymentInformation::BlockNumber(40884126)),
1359+
},
1360+
)
1361+
.add_network(
1362+
MAINNET,
1363+
Network {
1364+
address: addr("0xDaa273AeEc06e9CCb7428a77E2abb1E4659B16D2"),
1365+
// <https://etherscan.io/tx/0x0e1c9630dd44a7e1d5c958b9e5d9c9e0b45888e54b9b3d24424675e849dc95e7>
1366+
deployment_information: Some(DeploymentInformation::BlockNumber(22832233)),
1367+
},
1368+
)
1369+
.add_network(
1370+
OPTIMISM,
1371+
Network {
1372+
address: addr("0x891EC9B34829276a9a8ef2F8A9cEAF2486017e0d"),
1373+
// <https://optimistic.etherscan.io/tx/0xffb48a81db20156058aa6f81bbd8d53411887fd2e3a02f9fa24f4a3c748982cc>
1374+
deployment_information: Some(DeploymentInformation::BlockNumber(137934460)),
1375+
},
1376+
)
1377+
.add_network(
1378+
SEPOLIA,
1379+
Network {
1380+
address: addr("0xf58A574530Ea5cEB727095e6039170c1e8068fcA"),
1381+
// <https://sepolia.etherscan.io/tx/0xea5bbc9c461d578510096fcbc6ab0b3c78f1ff5c2343c34cb0848d9397a26e4e>
1382+
deployment_information: Some(DeploymentInformation::BlockNumber(8676768)),
1383+
},
1384+
)
1385+
});
13281386
generate_contract("BalancerV3WeightedPool");
13291387
generate_contract("BalancerV3StablePool");
13301388
generate_contract("BalancerV3GyroECLPPool");
1389+
generate_contract("BalancerV3ReClammPool");
13311390
generate_contract("IRateProvider");
13321391

13331392
generate_contract_with_config("BaoswapRouter", |builder| {

crates/contracts/src/bin/vendor.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ fn run() -> Result<()> {
155155
.github(
156156
"BalancerV3GyroECLPPoolFactory",
157157
"balancer/balancer-deployments/7a16f4463fb33d5f8c63118b6427b73fdb727cfc/v3/tasks/20250124-v3-gyro-eclp/artifact/GyroECLPPoolFactory.json",
158+
)?
159+
.github(
160+
"BalancerV3ReClammPoolFactoryV2",
161+
"balancer/balancer-deployments/18839b81d136315c587011c135958b49272547ab/v3/tasks/20250702-v3-reclamm-pool-v2/artifact/ReClammPoolFactory.json",
158162
)?;
159163

160164
// Balancer V3 contracts - ABI Only
@@ -171,6 +175,10 @@ fn run() -> Result<()> {
171175
.github(
172176
"BalancerV3GyroECLPPool",
173177
"balancer/balancer-deployments/7a16f4463fb33d5f8c63118b6427b73fdb727cfc/v3/tasks/20250124-v3-gyro-eclp/artifact/GyroECLPPool.json",
178+
)?
179+
.github(
180+
"BalancerV3ReClammPool",
181+
"balancer/balancer-deployments/18839b81d136315c587011c135958b49272547ab/v3/tasks/20250702-v3-reclamm-pool-v2/artifact/ReClammPool.json",
174182
)?;
175183

176184
// CowSwap contracts - Full

crates/contracts/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ include_contracts! {
8181
BalancerV3StablePoolFactoryV2;
8282
BalancerV3GyroECLPPool;
8383
BalancerV3GyroECLPPoolFactory;
84+
BalancerV3ReClammPool;
85+
BalancerV3ReClammPoolFactoryV2;
8486
BaoswapRouter;
8587
CowAmm;
8688
CowAmmConstantProductFactory;

crates/driver/src/boundary/liquidity/balancer/v3/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ use {
3838
};
3939

4040
pub mod gyro_e;
41+
pub mod reclamm;
4142
pub mod stable;
4243
pub mod weighted;
4344

@@ -180,6 +181,18 @@ async fn init_liquidity(
180181
)
181182
})
182183
.collect::<Vec<_>>(),
184+
config
185+
.reclamm
186+
.iter()
187+
.map(|&factory| {
188+
(
189+
BalancerFactoryKind::ReClamm,
190+
contracts::BalancerV3ReClammPoolFactoryV2::at(&web3, factory.into())
191+
.raw_instance()
192+
.clone(),
193+
)
194+
})
195+
.collect::<Vec<_>>(),
183196
]
184197
.into_iter()
185198
.flatten()
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use {
2+
crate::{
3+
boundary::Result,
4+
domain::{
5+
eth,
6+
liquidity::{self, balancer},
7+
},
8+
},
9+
solver::liquidity::{BalancerV3ReClammOrder, balancer_v3},
10+
};
11+
12+
/// Median gas used per BalancerV3SwapGivenOutInteraction.
13+
// TODO: Estimate actual gas usage for Balancer V3
14+
const GAS_PER_SWAP: u64 = 100_000;
15+
16+
pub fn to_domain(id: liquidity::Id, pool: BalancerV3ReClammOrder) -> Result<liquidity::Liquidity> {
17+
Ok(liquidity::Liquidity {
18+
id,
19+
gas: GAS_PER_SWAP.into(),
20+
kind: liquidity::Kind::BalancerV3ReClamm(balancer::v3::reclamm::Pool {
21+
batch_router: batch_router(&pool),
22+
id: pool_id(&pool),
23+
reserves: balancer::v3::reclamm::Reserves::try_new(
24+
pool.reserves
25+
.into_iter()
26+
.map(|(token, reserve)| {
27+
Ok(balancer::v3::reclamm::Reserve {
28+
asset: eth::Asset {
29+
token: token.into(),
30+
amount: reserve.balance.into(),
31+
},
32+
scale: balancer::v3::ScalingFactor::from_raw(
33+
reserve.scaling_factor.as_uint256(),
34+
)?,
35+
})
36+
})
37+
.collect::<Result<_>>()?,
38+
)?,
39+
fee: balancer::v3::Fee::from_raw(pool.fee.as_uint256()),
40+
version: match pool.version {
41+
shared::sources::balancer_v3::pools::reclamm::Version::V2 => {
42+
balancer::v3::reclamm::Version::V2
43+
}
44+
},
45+
last_virtual_balances: pool.last_virtual_balances.into_iter().collect(),
46+
daily_price_shift_base: balancer::v3::ScalingFactor::from_raw(
47+
pool.daily_price_shift_base.as_uint256(),
48+
)?,
49+
last_timestamp: pool.last_timestamp,
50+
centeredness_margin: balancer::v3::ScalingFactor::from_raw(
51+
pool.centeredness_margin.as_uint256(),
52+
)?,
53+
start_fourth_root_price_ratio: balancer::v3::ScalingFactor::from_raw(
54+
pool.start_fourth_root_price_ratio.as_uint256(),
55+
)?,
56+
end_fourth_root_price_ratio: balancer::v3::ScalingFactor::from_raw(
57+
pool.end_fourth_root_price_ratio.as_uint256(),
58+
)?,
59+
price_ratio_update_start_time: pool.price_ratio_update_start_time,
60+
price_ratio_update_end_time: pool.price_ratio_update_end_time,
61+
}),
62+
})
63+
}
64+
65+
pub fn to_interaction(
66+
pool: &liquidity::balancer::v3::reclamm::Pool,
67+
input: &liquidity::MaxInput,
68+
output: &liquidity::ExactOutput,
69+
receiver: &eth::Address,
70+
) -> eth::Interaction {
71+
super::to_interaction(
72+
&super::Pool {
73+
batch_router: pool.batch_router,
74+
id: pool.id,
75+
},
76+
input,
77+
output,
78+
receiver,
79+
)
80+
}
81+
82+
fn batch_router(pool: &BalancerV3ReClammOrder) -> eth::ContractAddress {
83+
pool.settlement_handling
84+
.as_any()
85+
.downcast_ref::<balancer_v3::SettlementHandler>()
86+
.expect("downcast balancer v3 settlement handler")
87+
.batch_router()
88+
.address()
89+
.into()
90+
}
91+
92+
fn pool_id(pool: &BalancerV3ReClammOrder) -> balancer::v3::Id {
93+
pool.settlement_handling
94+
.as_any()
95+
.downcast_ref::<balancer_v3::SettlementHandler>()
96+
.expect("downcast balancer v3 settlement handler")
97+
.pool_id()
98+
.into()
99+
}

crates/driver/src/boundary/liquidity/erc4626.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use {
55
infra::blockchain::Ethereum,
66
},
77
anyhow::Result as AnyResult,
8+
chain::Chain,
89
shared::sources::erc4626::registry::Erc4626Registry,
910
solver::{
1011
liquidity::erc4626::{Erc4626LiquiditySource, Erc4626Order},
@@ -13,14 +14,30 @@ use {
1314
std::time::Duration,
1415
};
1516

17+
/// Maps chain names to their directory names in the configs folder
18+
fn chain_to_config_dir(chain: &Chain) -> &'static str {
19+
match chain {
20+
Chain::ArbitrumOne => "arbitrum",
21+
Chain::Mainnet => "mainnet",
22+
Chain::Goerli => "goerli",
23+
Chain::Sepolia => "sepolia",
24+
Chain::Gnosis => "gnosis",
25+
Chain::Base => "base",
26+
Chain::Bnb => "bnb",
27+
Chain::Avalanche => "avalanche",
28+
Chain::Optimism => "optimism",
29+
Chain::Polygon => "polygon",
30+
Chain::Hardhat => "hardhat",
31+
}
32+
}
33+
1634
/// Builds the ERC4626 liquidity collector if enabled via
1735
/// configs/<chain>/erc4626.toml.
1836
pub async fn maybe_collector(eth: &Ethereum) -> AnyResult<Vec<Box<dyn LiquidityCollecting>>> {
1937
// Try to load per-chain config file; if missing or disabled, return empty.
2038
let chain = eth.chain();
21-
let chain_str = format!("{:?}", chain);
22-
let chain_lc = chain_str.to_lowercase();
23-
let primary_path = format!("configs/{}/erc4626.toml", chain_lc);
39+
let config_dir = chain_to_config_dir(&chain);
40+
let primary_path = format!("configs/{}/erc4626.toml", config_dir);
2441
let fallback_path = format!("../{}", primary_path);
2542
let web3 = boundary::web3(eth);
2643
let settlement = eth.contracts().settlement().clone();

crates/driver/src/boundary/liquidity/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ impl Fetcher {
194194
Liquidity::BalancerV3Stable(pool) => balancer::v3::stable::to_domain(id, pool),
195195
Liquidity::BalancerGyroE(pool) => balancer::v2::gyro_e::to_domain(id, pool),
196196
Liquidity::BalancerV3GyroE(pool) => balancer::v3::gyro_e::to_domain(id, pool),
197+
Liquidity::BalancerV3ReClamm(pool) => balancer::v3::reclamm::to_domain(id, pool),
197198
Liquidity::LimitOrder(pool) => zeroex::to_domain(id, pool),
198199
Liquidity::Concentrated(pool) => uniswap::v3::to_domain(id, pool),
199200
Liquidity::Erc4626(order) => erc4626::to_domain(id, order),

crates/driver/src/domain/competition/solution/encoding.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,9 @@ pub fn liquidity_interaction(
416416
liquidity::Kind::BalancerV3GyroE(pool) => pool
417417
.swap(&input, &output, &settlement.address().into())
418418
.ok(),
419+
liquidity::Kind::BalancerV3ReClamm(pool) => pool
420+
.swap(&input, &output, &settlement.address().into())
421+
.ok(),
419422
liquidity::Kind::Swapr(pool) => pool
420423
.swap(&input, &output, &settlement.address().into())
421424
.ok(),

0 commit comments

Comments
 (0)