Skip to content

Commit

Permalink
Add Sepolia testnet (#2167)
Browse files Browse the repository at this point in the history
# Description

This PR provides everything needed for making the services work on
Sepolia. This almost exclusively means populating the contract crate
with relevant addresses on Sepolia and directly related changes.

The main problem with Sepolia is the lack of liquidity, even compared to
Görli. The only liquidity source we have available is a third-party
Uniswap v2 deployment where I deployed some liquidity myself.
We also have Balancer, but I need to sort things out with the subgraph
URL (see [2139](#2139)) so
this will happen in a later PR.

# Changes

- New contract addresses for Sepolia.
- New Uniswapv2-like liquidity source on Sepolia (`TestnetUniswapV2`)
and related ignored test. Router, factory and pair are exactly the same
as on vanilla mainnet except for two things:
- The Sepolia factory has an extra line `bytes32 public constant
INIT_CODE_HASH =
keccak256(abi.encodePacked(type(UniswapV2Pair).creationCode));`.
- the [compilation
metadata](https://docs.soliditylang.org/en/v0.8.23/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode)
is different, which in turn causes the init hash to be different, but
this has no impact on the actual functionality of the contract.
- Removed `SolverTrampoline`. It was introduced
[here](704fb32)
for [this
feature](8b875ed)
but it wasn't removed when that feature was removed
[here](2ec435c).
- New comment on how to compute the init code hash of an unknown
Uniswapv2 deployment.
- Removed broken Uniswapv2 address on Gnosis Chain (this is address
`0x1b02da8cb0d097eb8d57a175b88c7d8b47997506`, which is actually the
_Sushiswap_ address and since the init code hash is different it
wouldn't be working if enabled).
- Update domain separator test to use new deployment on Sepolia.

## Not included

- Update API urls to include Sepolia (will do that once the API is
available).
- Balancer support (because of the subgraph issue).
- Univ3 liquidity (available but I didn't find a subgraph we can trust
and I'm unsure what happens if we use an unreliable subgraph).

## How to test

Try to run the entire setup locally. I tried with autopilot, orderbook,
baseline solver and driver in colocation mode.
[Here](https://sepolia.etherscan.io/tx/0x545bc8df347f2859a45b5e3d869b39c8eea7b9a08be30e7d91a0508152774821)
is an executed settlement.

<details><summary>Here is what I did.</summary>


Create the following files.

<details><summary>./sepolia-addressbook-orderbook.env</summary>

```
export NODE_URL='https://ethereum-sepolia.publicnode.com'
export NATIVE_PRICE_ESTIMATORS='Baseline'
export GAS_ESTIMATORS=native
weth_balancer='0x7b79995e5f793a07bc00c21412e50ecae098e7f9'
weth_uniswap='0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14'
export BASE_TOKENS="$weth_uniswap"
export BASELINE_SOURCES='TestnetUniswapV2'
export CHAIN_ID=11155111
export AMOUNT_TO_ESTIMATE_PRICES_WITH=100000000000000000
export ETHFLOW_CONTRACT='0x2671994c7D224ac4799ac2cf6Ef9EF187d42C69f'
export ETHFLOW_INDEXING_START='4718695'
export DRIVERS='baseline|http://127.0.0.1:12345/baseline/'
export ENABLE_COLOCATION=true
```
</details>


<details><summary>./sepolia-baseline-solver-engine.toml</summary>

```
base-tokens = ['0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14']
chain-id = '11155111'
max-hops = 1
max-partial-attempts = 5
risk-parameters = [0, 0, 0, 30]
```
</details>

<details><summary>./sepolia-driver.toml (but you need to add a valid
solver secret key here, search in 1password for "Sepolia test solver
PK")</summary>

```
[contracts]

[liquidity]
base-tokens = ['0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14']

[[liquidity.uniswap-v2]]
preset = 'testnet-uniswap-v2'

[[solver]]
account = '0x77927c2c3E6977badeAB8bd1d74051d1fEB36f88' # replace address with the actual secret key!
endpoint = 'http://127.0.0.2:23456'
name = 'baseline'
relative-slippage = '0.01'

[submission]
additional-tip-percentage = 0.05
gas-price-cap = 1000000000000.0
max-confirm-time-secs = 120

[[submission.mempool]]
mempool = 'public'
revert-protection = false
```
</details>

Run the following commands (each group in different terminals).

```sh
docker-compose up --build db migrations
```
(You can clear everything with with `docker-compose down
--remove-orphans --volumes`.)

```sh
cargo run --bin solvers -- --addr 127.0.0.2:23456 baseline --config ./sepolia-baseline-solver-engine.toml
```

```sh
cargo run --bin driver -- --config ./sepolia-driver.toml --addr '127.0.0.1:12345' --ethrpc 'https://ethereum-sepolia.publicnode.com'
```

```sh
source sepolia-autopilot-orderbook.env  && cargo run --bin orderbook
```

```sh
source sepolia-autopilot-orderbook.env  && cargo run --bin autopilot
```

To create a new order I used the code
[here](https://github.com/cowprotocol/contracts/tree/test-sepolia-order).
Install and run `export PK=<your own pk>; bash order.sh` after getting a
few WETH (at least 2, find them in the team test fund safe
[here](https://app.safe.global/transactions/queue?safe=sep:0x01B94B667236a7896aC85D5bccdF23f26b10e6Cc))
and approving the vault relayer.

</details>
  • Loading branch information
fedgiac authored Dec 15, 2023
1 parent c8dcee6 commit c263173
Show file tree
Hide file tree
Showing 16 changed files with 243 additions and 30 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ Run an `autopilot` with:

```sh
cargo run --bin autopilot -- \
--skip-event-sync \
--skip-event-sync true \
--node-url <YOUR_NODE_URL>
```

Expand Down
1 change: 1 addition & 0 deletions crates/contracts/artifacts/TestnetUniswapV2Router02.json
153 changes: 147 additions & 6 deletions crates/contracts/build.rs

Large diffs are not rendered by default.

26 changes: 17 additions & 9 deletions crates/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ include_contracts! {
IUniswapV3Factory;
IZeroEx;
PancakeRouter;
SolverTrampoline;
SushiSwapRouter;
SwaprRouter;
TestnetUniswapV2Router02;
UniswapV2Factory;
UniswapV2Router02;
UniswapV3Pool;
Expand Down Expand Up @@ -93,6 +93,7 @@ mod tests {
const MAINNET: u64 = 1;
const GOERLI: u64 = 5;
const GNOSIS: u64 = 100;
const SEPOLIA: u64 = 11155111;

use {
super::*,
Expand Down Expand Up @@ -155,34 +156,41 @@ mod tests {
}};
}

for network in &[MAINNET, GOERLI, GNOSIS] {
for network in &[MAINNET, GOERLI, GNOSIS, SEPOLIA] {
assert_has_deployment_address!(GPv2Settlement for *network);
assert_has_deployment_address!(SushiSwapRouter for *network);
assert_has_deployment_address!(WETH9 for *network);
assert_has_deployment_address!(CowProtocolToken for *network);
assert_has_deployment_address!(HooksTrampoline for *network);
assert_has_deployment_address!(BalancerV2Vault for *network);
assert_has_deployment_address!(BalancerV2NoProtocolFeeLiquidityBootstrappingPoolFactory for *network);
}
for network in &[MAINNET, GOERLI, GNOSIS] {
assert_has_deployment_address!(SushiSwapRouter for *network);
}
for network in &[MAINNET, GOERLI, SEPOLIA] {
assert_has_deployment_address!(UniswapV3SwapRouter for *network);
assert_has_deployment_address!(IUniswapV3Factory for *network);
}
for network in &[MAINNET, GOERLI] {
assert_has_deployment_address!(BalancerV2Vault for *network);
assert_has_deployment_address!(BalancerV2WeightedPoolFactory for *network);
assert_has_deployment_address!(BalancerV2WeightedPool2TokensFactory for *network);
assert_has_deployment_address!(UniswapV2Factory for *network);
assert_has_deployment_address!(UniswapV2Router02 for *network);
assert_has_deployment_address!(UniswapV3SwapRouter for *network);
assert_has_deployment_address!(IUniswapV3Factory for *network);
}

// only mainnet
assert_has_deployment_address!(BalancerV2StablePoolFactoryV2 for MAINNET);
assert_has_deployment_address!(BalancerV2LiquidityBootstrappingPoolFactory for MAINNET);
assert_has_deployment_address!(BalancerV2NoProtocolFeeLiquidityBootstrappingPoolFactory for MAINNET);
assert_has_deployment_address!(PancakeRouter for MAINNET);
assert_has_deployment_address!(IZeroEx for MAINNET);

// only gnosis
assert_has_deployment_address!(BaoswapRouter for GNOSIS);
assert_has_deployment_address!(HoneyswapRouter for GNOSIS);
assert_has_deployment_address!(SwaprRouter for GNOSIS);

// only sepolia
assert_has_deployment_address!(TestnetUniswapV2Router02 for SEPOLIA);
}

#[test]
Expand All @@ -198,11 +206,11 @@ mod tests {
}};
}

for network in &[MAINNET, GOERLI, GNOSIS] {
for network in &[MAINNET, GOERLI, GNOSIS, SEPOLIA] {
assert_has_deployment_information!(GPv2Settlement for *network);
assert_has_deployment_information!(BalancerV2Vault for *network);
}
for network in &[MAINNET, GOERLI] {
assert_has_deployment_information!(BalancerV2Vault for *network);
assert_has_deployment_information!(BalancerV2WeightedPoolFactory for *network);
assert_has_deployment_information!(BalancerV2WeightedPool2TokensFactory for *network);
}
Expand Down
3 changes: 3 additions & 0 deletions crates/driver/src/infra/config/file/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ pub async fn load(network: &blockchain::Network, path: &Path) -> infra::Config {
file::UniswapV2Preset::PancakeSwap => {
liquidity::config::UniswapV2::pancake_swap(&network.id)
}
file::UniswapV2Preset::TestnetUniswapV2 => {
liquidity::config::UniswapV2::testnet_uniswapv2(&network.id)
}
}
.expect("no Uniswap V2 preset for current network"),
file::UniswapV2Config::Manual {
Expand Down
1 change: 1 addition & 0 deletions crates/driver/src/infra/config/file/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ enum UniswapV2Preset {
Honeyswap,
Baoswap,
PancakeSwap,
TestnetUniswapV2,
}

#[derive(Clone, Debug, Deserialize)]
Expand Down
14 changes: 14 additions & 0 deletions crates/driver/src/infra/liquidity/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,20 @@ impl UniswapV2 {
missing_pool_cache_time: Duration::from_secs(60 * 60),
})
}

/// Returns the liquidity configuration for liquidity sources only used on
/// test networks.
pub fn testnet_uniswapv2(network: &eth::NetworkId) -> Option<Self> {
Some(Self {
router: deployment_address(
contracts::TestnetUniswapV2Router02::raw_contract(),
network,
)?,
pool_code: hex!("0efd7612822d579e24a8851501d8c2ad854264a1050e3dfcee8afcca08f80a86")
.into(),
missing_pool_cache_time: Duration::from_secs(60 * 60),
})
}
}

/// Swapr (Uniswap V2 clone with a twist) liquidity fetching options.
Expand Down
13 changes: 7 additions & 6 deletions crates/model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,16 +165,17 @@ mod tests {
}

#[test]
fn domain_separator_goerli() {
fn domain_separator_sepolia() {
let contract_address: H160 = hex!("9008D19f58AAbD9eD0D60971565AA8510560ab41").into(); // new deployment
let chain_id: u64 = 5;
let domain_separator_goerli = DomainSeparator::new(chain_id, contract_address);
// domain separator is taken from goerli deployment at address
let chain_id: u64 = 11155111;
let domain_separator_sepolia = DomainSeparator::new(chain_id, contract_address);
// domain separator is taken from Sepolia deployment at address
// 0x9008D19f58AAbD9eD0D60971565AA8510560ab41
// https://sepolia.etherscan.io/address/0x9008d19f58aabd9ed0d60971565aa8510560ab41#readContract#F2
let expected_domain_separator = DomainSeparator(hex!(
"fb378b35457022ecc5709ae5dafad9393c1387ae6d8ce24913a0c969074c07fb"
"daee378bd0eb30ddf479272accf91761e697bc00e067a268f95f1d2732ed230b"
));
assert_eq!(domain_separator_goerli, expected_domain_separator);
assert_eq!(domain_separator_sepolia, expected_domain_separator);
}

#[test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ impl BlockscoutTokenOwnerFinder {
1 => "https://eth.blockscout.com/api",
5 => "https://eth-goerli.blockscout.com/api",
100 => "https://blockscout.com/xdai/mainnet/api",
11155111 => "https://eth-sepolia.blockscout.com/api",
_ => bail!("Unsupported Network"),
};

Expand Down
1 change: 1 addition & 0 deletions crates/shared/src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub fn block_interval(network_id: &str, chain_id: u64) -> Option<Duration> {
("1", 1) => 12,
("5", 5) => 12,
("100", 100) => 5,
("11155111", 11155111) => 12,
_ => return None,
}))
}
4 changes: 2 additions & 2 deletions crates/shared/src/price_estimation/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ pub type NativePriceEstimateResult = Result<f64, PriceEstimationError>;

pub fn default_amount_to_estimate_native_prices_with(chain_id: u64) -> Option<U256> {
match chain_id {
// Mainnet, Göŕli
1 | 5 => Some(10u128.pow(18).into()),
// Mainnet, Göŕli, Sepolia
1 | 5 | 11155111 => Some(10u128.pow(18).into()),
// Gnosis chain
100 => Some(10u128.pow(21).into()),
_ => None,
Expand Down
7 changes: 2 additions & 5 deletions crates/shared/src/sources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub enum BaselineSource {
Swapr,
ZeroEx,
UniswapV3,
TestnetUniswapV2,
}

pub fn defaults_for_chain(chain_id: u64) -> Result<Vec<BaselineSource>> {
Expand All @@ -38,11 +39,6 @@ pub fn defaults_for_chain(chain_id: u64) -> Result<Vec<BaselineSource>> {
BaselineSource::ZeroEx,
BaselineSource::UniswapV3,
],
4 => vec![
BaselineSource::UniswapV2,
BaselineSource::SushiSwap,
BaselineSource::BalancerV2,
],
5 => vec![
BaselineSource::UniswapV2,
BaselineSource::SushiSwap,
Expand All @@ -54,6 +50,7 @@ pub fn defaults_for_chain(chain_id: u64) -> Result<Vec<BaselineSource>> {
BaselineSource::Baoswap,
BaselineSource::Swapr,
],
11155111 => vec![BaselineSource::TestnetUniswapV2],
_ => bail!("unsupported chain {:#x}", chain_id),
})
}
Expand Down
6 changes: 6 additions & 0 deletions crates/shared/src/sources/balancer_v2/pool_fetching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,12 @@ impl BalancerFactoryKind {
Self::ComposableStableV4,
Self::ComposableStableV5,
],
11155111 => vec![
Self::WeightedV4,
Self::ComposableStableV4,
Self::ComposableStableV5,
Self::NoProtocolFeeLiquidityBootstrapping,
],
_ => Default::default(),
}
}
Expand Down
36 changes: 36 additions & 0 deletions crates/shared/src/sources/uniswap_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ use {
std::{fmt::Display, str::FromStr, sync::Arc},
};

// How to compute for unknown contracts
// Find a pair creation transaction and open it with Tenderly on the debugger
// page. Example:
// https://dashboard.tenderly.co/tx/sepolia/0x4d31daa9e74b96a5c9a780cf8839b115ac25127b17226ecb1ad6e7f244fd1c8f/debugger?trace=0.1
// Find the CREATE2 step and take the "input" value in the debugger box; this
// is the init code. Trim 0x and hash the resulting hex-encoded bytestring, for
// example with `xxd -ps -r < ./initcode.txt | openssl dgst -keccak-256` (with
// Openssl version ≥3.2) or https://emn178.github.io/online-tools/keccak_256.html
pub const UNISWAP_INIT: [u8; 32] =
hex!("96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f");
pub const HONEYSWAP_INIT: [u8; 32] =
Expand All @@ -30,6 +38,8 @@ pub const BAOSWAP_INIT: [u8; 32] =
hex!("0bae3ead48c325ce433426d2e8e6b07dac10835baec21e163760682ea3d3520d");
pub const SWAPR_INIT: [u8; 32] =
hex!("d306a548755b9295ee49cc729e13ca4a45e00199bbd890fa146da43a50571776");
pub const TESTNET_UNISWAP_INIT: [u8; 32] =
hex!("0efd7612822d579e24a8851501d8c2ad854264a1050e3dfcee8afcca08f80a86");

#[derive(Debug, Clone, Copy)]
pub struct UniV2BaselineSourceParameters {
Expand Down Expand Up @@ -80,6 +90,11 @@ impl UniV2BaselineSourceParameters {
SWAPR_INIT,
PoolReadingStyle::Swapr,
)),
BS::TestnetUniswapV2 => Some((
contracts::TestnetUniswapV2Router02::raw_contract(),
TESTNET_UNISWAP_INIT,
PoolReadingStyle::Default,
)),
}?;
Some(Self {
router: contract.networks.get(net_version)?.address,
Expand Down Expand Up @@ -232,6 +247,27 @@ mod tests {
.await;
}

#[tokio::test]
#[ignore]
async fn baseline_sepolia() {
let http = crate::ethrpc::create_env_test_transport();
let web3 = Web3::new(http);
let version = web3.net().version().await.unwrap();
assert_eq!(version, "11155111", "test must be run with mainnet node");
let test = |source, token0, token1, expected| {
test_baseline_source(&web3, "11155111", source, token0, token1, expected)
};

// https://sepolia.etherscan.io/tx/0x4d31daa9e74b96a5c9a780cf8839b115ac25127b17226ecb1ad6e7f244fd1c8f
test(
BaselineSource::TestnetUniswapV2,
addr!("fff9976782d46cc05630d1f6ebab18b2324d6b14"),
addr!("7c43482436624585c27cc9f804e53463d5a37aba"),
addr!("84A1CE0e56500D51a6a6e2559567007E26dc8a7C"),
)
.await;
}

#[tokio::test]
#[ignore]
async fn baseline_xdai() {
Expand Down
2 changes: 1 addition & 1 deletion crates/solver/src/settlement_submission/submitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pub struct SubmitterParams {
pub deadline: Option<Instant>,
/// Re-simulate and resend transaction on every retry_interval seconds
pub retry_interval: Duration,
/// Network id (mainnet, rinkeby, goerli, gnosis chain)
/// Network id (mainnet, goerli, sepolia, gnosis chain)
pub network_id: String,
/// Additional bytes to append to the call data. This is required by the
/// `driver`.
Expand Down
3 changes: 3 additions & 0 deletions crates/solvers/src/domain/eth/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub enum ChainId {
Mainnet = 1,
Goerli = 5,
Gnosis = 100,
Sepolia = 11155111,
}

impl ChainId {
Expand All @@ -21,6 +22,7 @@ impl ChainId {
1 => Ok(Self::Mainnet),
5 => Ok(Self::Goerli),
100 => Ok(Self::Gnosis),
11155111 => Ok(Self::Sepolia),
_ => Err(UnsupportedChain),
}
}
Expand All @@ -31,6 +33,7 @@ impl ChainId {
ChainId::Mainnet => "1",
ChainId::Goerli => "5",
ChainId::Gnosis => "100",
ChainId::Sepolia => "11155111",
}
}

Expand Down

0 comments on commit c263173

Please sign in to comment.