Skip to content

Commit

Permalink
test: consolidate RPC URLs, remove flaky ones (#8534)
Browse files Browse the repository at this point in the history
* test: consolidate RPC URLs, remove flaky ones

* chore: clippy
  • Loading branch information
DaniPopes authored Jul 26, 2024
1 parent f5e4ec8 commit 0ade1fd
Show file tree
Hide file tree
Showing 41 changed files with 152 additions and 130 deletions.
7 changes: 7 additions & 0 deletions .github/scripts/format.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -eo pipefail

# We have to ignore at shell level because testdata/ is not a valid Foundry project,
# so running `forge fmt` with `--root testdata` won't actually check anything
shopt -s extglob
cargo run --bin forge -- fmt "$@" $(find testdata -name '*.sol' ! -name Vm.sol)
6 changes: 1 addition & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,7 @@ jobs:
cache-on-failure: true
- name: forge fmt
shell: bash
# We have to ignore at shell level because testdata/ is not a valid Foundry project,
# so running `forge fmt` with `--root testdata` won't actually check anything
run: |
shopt -s extglob
cargo run --bin forge -- fmt --check $(find testdata -name '*.sol' ! -name Vm.sol)
run: ./.github/scripts/format.sh --check

crate-checks:
runs-on: ubuntu-latest
Expand Down
4 changes: 2 additions & 2 deletions crates/forge/tests/it/fork.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ async fn test_storage_caching_config() {
Filter::new("testStorageCaching", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork"))
.exclude_tests(".*Revert");
TestConfig::with_filter(runner, filter).run().await;
let cache_dir = Config::foundry_block_cache_dir(Chain::mainnet(), 19800000);
assert!(!cache_dir.unwrap().exists());
let cache_dir = Config::foundry_block_cache_dir(Chain::mainnet(), 19800000).unwrap();
let _ = fs::remove_file(cache_dir);

// no_storage_caching set to false: storage should be cached
let mut config = TEST_DATA_DEFAULT.config.clone();
Expand Down
24 changes: 10 additions & 14 deletions crates/forge/tests/it/test_helpers.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Test helpers for Forge integration tests.
use alloy_chains::NamedChain;
use alloy_primitives::U256;
use forge::{
revm::primitives::SpecId, MultiContractRunner, MultiContractRunnerBuilder, TestOptions,
Expand All @@ -18,7 +19,7 @@ use foundry_evm::{
constants::CALLER,
opts::{Env, EvmOpts},
};
use foundry_test_utils::{fd_lock, init_tracing};
use foundry_test_utils::{fd_lock, init_tracing, rpc::next_rpc_endpoint};
use once_cell::sync::Lazy;
use std::{
env, fmt,
Expand Down Expand Up @@ -357,18 +358,13 @@ pub fn manifest_root() -> &'static Path {
/// the RPC endpoints used during tests
pub fn rpc_endpoints() -> RpcEndpoints {
RpcEndpoints::new([
(
"rpcAlias",
RpcEndpoint::Url(
"https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf".to_string(),
),
),
(
"rpcAliasSepolia",
RpcEndpoint::Url(
"https://eth-sepolia.g.alchemy.com/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf".to_string(),
),
),
("rpcEnvAlias", RpcEndpoint::Env("${RPC_ENV_ALIAS}".to_string())),
("mainnet", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Mainnet))),
("mainnet2", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Mainnet))),
("sepolia", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Sepolia))),
("optimism", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Optimism))),
("arbitrum", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Arbitrum))),
("polygon", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Polygon))),
("avaxTestnet", RpcEndpoint::Url("https://api.avax-test.network/ext/bc/C/rpc".into())),
("rpcEnvAlias", RpcEndpoint::Env("${RPC_ENV_ALIAS}".into())),
])
}
85 changes: 61 additions & 24 deletions crates/test-utils/src/rpc.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! RPC API keys utilities.
use foundry_config::NamedChain;
use once_cell::sync::Lazy;
use rand::seq::SliceRandom;
use std::sync::atomic::{AtomicUsize, Ordering};
Expand All @@ -18,7 +19,7 @@ static INFURA_KEYS: Lazy<Vec<&'static str>> = Lazy::new(|| {
});

// List of alchemy keys for mainnet
static ALCHEMY_MAINNET_KEYS: Lazy<Vec<&'static str>> = Lazy::new(|| {
static ALCHEMY_KEYS: Lazy<Vec<&'static str>> = Lazy::new(|| {
let mut keys = vec![
"ib1f4u1ojm-9lJJypwkeZeG-75TJRB7O",
"7mTtk6IW4DwroGnKmG_bOWri2hyaGYhX",
Expand All @@ -44,6 +45,9 @@ static ALCHEMY_MAINNET_KEYS: Lazy<Vec<&'static str>> = Lazy::new(|| {
"sDNCLu_e99YZRkbWlVHiuM3BQ5uxYCZU",
"M6lfpxTBrywHOvKXOS4yb7cTTpa25ZQ9",
"UK8U_ogrbYB4lQFTGJHHDrbiS4UPnac6",
"Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf",
"UVatYU2Ax0rX6bDiqddeTRDdcCxzdpoE",
"bVjX9v-FpmUhf5R_oHIgwJx2kXvYPRbx",
];

keys.shuffle(&mut rand::thread_rng());
Expand Down Expand Up @@ -79,55 +83,43 @@ fn next() -> usize {
}

fn num_keys() -> usize {
INFURA_KEYS.len() + ALCHEMY_MAINNET_KEYS.len()
INFURA_KEYS.len() + ALCHEMY_KEYS.len()
}

/// Returns the next _mainnet_ rpc endpoint in inline
///
/// This will rotate all available rpc endpoints
pub fn next_http_rpc_endpoint() -> String {
next_rpc_endpoint("mainnet")
next_rpc_endpoint(NamedChain::Mainnet)
}

/// Returns the next _mainnet_ rpc endpoint in inline
///
/// This will rotate all available rpc endpoints
pub fn next_ws_rpc_endpoint() -> String {
next_ws_endpoint("mainnet")
next_ws_endpoint(NamedChain::Mainnet)
}

/// Returns the next HTTP RPC endpoint.
pub fn next_rpc_endpoint(network: &str) -> String {
let idx = next() % num_keys();
if idx < INFURA_KEYS.len() {
format!("https://{network}.infura.io/v3/{}", INFURA_KEYS[idx])
} else {
let idx = idx - INFURA_KEYS.len();
format!("https://eth-{network}.alchemyapi.io/v2/{}", ALCHEMY_MAINNET_KEYS[idx])
}
pub fn next_rpc_endpoint(chain: NamedChain) -> String {
next_url(false, chain)
}

/// Returns the next WS RPC endpoint.
pub fn next_ws_endpoint(network: &str) -> String {
let idx = next() % num_keys();
if idx < INFURA_KEYS.len() {
format!("wss://{network}.infura.io/v3/{}", INFURA_KEYS[idx])
} else {
let idx = idx - INFURA_KEYS.len();
format!("wss://eth-{network}.alchemyapi.io/v2/{}", ALCHEMY_MAINNET_KEYS[idx])
}
pub fn next_ws_endpoint(chain: NamedChain) -> String {
next_url(true, chain)
}

/// Returns endpoint that has access to archive state
pub fn next_http_archive_rpc_endpoint() -> String {
let idx = next() % ALCHEMY_MAINNET_KEYS.len();
format!("https://eth-mainnet.alchemyapi.io/v2/{}", ALCHEMY_MAINNET_KEYS[idx])
let idx = next() % ALCHEMY_KEYS.len();
format!("https://eth-mainnet.g.alchemy.com/v2/{}", ALCHEMY_KEYS[idx])
}

/// Returns endpoint that has access to archive state
pub fn next_ws_archive_rpc_endpoint() -> String {
let idx = next() % ALCHEMY_MAINNET_KEYS.len();
format!("wss://eth-mainnet.alchemyapi.io/v2/{}", ALCHEMY_MAINNET_KEYS[idx])
let idx = next() % ALCHEMY_KEYS.len();
format!("wss://eth-mainnet.g.alchemy.com/v2/{}", ALCHEMY_KEYS[idx])
}

/// Returns the next etherscan api key
Expand All @@ -136,6 +128,51 @@ pub fn next_etherscan_api_key() -> String {
ETHERSCAN_MAINNET_KEYS[idx].to_string()
}

fn next_url(is_ws: bool, chain: NamedChain) -> String {
use NamedChain::*;

let idx = next() % num_keys();
let is_infura = idx < INFURA_KEYS.len();

let key = if is_infura { INFURA_KEYS[idx] } else { ALCHEMY_KEYS[idx - INFURA_KEYS.len()] };

// Nowhere near complete.
let prefix = if is_infura {
match chain {
Optimism => "optimism",
Arbitrum => "arbitrum",
Polygon => "polygon",
_ => "",
}
} else {
match chain {
Optimism => "opt",
Arbitrum => "arb",
Polygon => "polygon",
_ => "eth",
}
};
let network = if is_infura {
match chain {
Mainnet | Optimism | Arbitrum | Polygon => "mainnet",
_ => chain.as_str(),
}
} else {
match chain {
Mainnet | Optimism | Arbitrum | Polygon => "mainnet",
_ => chain.as_str(),
}
};
let full = if prefix.is_empty() { network.to_string() } else { format!("{prefix}-{network}") };

match (is_ws, is_infura) {
(false, true) => format!("https://{full}.infura.io/v3/{key}"),
(true, true) => format!("wss://{full}.infura.io/v3/{key}"),
(false, false) => format!("https://{full}.g.alchemy.com/v2/{key}"),
(true, false) => format!("wss://{full}.g.alchemy.com/v2/{key}"),
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
12 changes: 6 additions & 6 deletions testdata/default/cheats/Fork.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ contract ForkTest is DSTest {

// this will create two _different_ forks during setup
function setUp() public {
forkA = vm.createFork("https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf", mainblock);
forkB = vm.createFork("https://eth-mainnet.alchemyapi.io/v2/9VWGraLx0tMiSWx05WH-ywgSVmMxs66W", mainblock - 1);
forkA = vm.createFork("mainnet", mainblock);
forkB = vm.createFork("mainnet2", mainblock - 1);
testValue = 999;
}

Expand All @@ -35,7 +35,7 @@ contract ForkTest is DSTest {

// ensures we can create and select in one step
function testCreateSelect() public {
uint256 fork = vm.createSelectFork("https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf");
uint256 fork = vm.createSelectFork("mainnet");
assertEq(fork, vm.activeFork());
}

Expand Down Expand Up @@ -114,12 +114,12 @@ contract ForkTest is DSTest {

// ensures forks change chain ids automatically
function testCanAutoUpdateChainId() public {
vm.createSelectFork("https://polygon-pokt.nodies.app"); // Polygon mainnet RPC URL
assertEq(block.chainid, 137);
vm.createSelectFork("sepolia");
assertEq(block.chainid, 11155111);
}

// ensures forks storage is cached at block
function testStorageCaching() public {
vm.createSelectFork("https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf", 19800000);
vm.createSelectFork("mainnet", 19800000);
}
}
14 changes: 7 additions & 7 deletions testdata/default/cheats/Fork2.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ contract ForkTest is DSTest {

// this will create two _different_ forks during setup
function setUp() public {
mainnetFork = vm.createFork("rpcAlias");
optimismFork = vm.createFork("https://opt-mainnet.g.alchemy.com/v2/UVatYU2Ax0rX6bDiqddeTRDdcCxzdpoE");
mainnetFork = vm.createFork("mainnet");
optimismFork = vm.createFork("optimism");
}

// ensures forks use different ids
Expand All @@ -57,7 +57,7 @@ contract ForkTest is DSTest {
}

function testCanCreateSelect() public {
uint256 anotherFork = vm.createSelectFork("rpcAlias");
uint256 anotherFork = vm.createSelectFork("mainnet");
assertEq(anotherFork, vm.activeFork());
}

Expand All @@ -75,12 +75,12 @@ contract ForkTest is DSTest {
// test that we can switch between forks, and "roll" blocks
function testCanRollFork() public {
vm.selectFork(mainnetFork);
uint256 otherMain = vm.createFork("rpcAlias", block.number - 1);
uint256 otherMain = vm.createFork("mainnet", block.number - 1);
vm.selectFork(otherMain);
uint256 mainBlock = block.number;

uint256 forkedBlock = 14608400;
uint256 otherFork = vm.createFork("rpcAlias", forkedBlock);
uint256 otherFork = vm.createFork("mainnet", forkedBlock);
vm.selectFork(otherFork);
assertEq(block.number, forkedBlock);

Expand All @@ -101,7 +101,7 @@ contract ForkTest is DSTest {
uint256 block = 16261704;

// fork until previous block
uint256 fork = vm.createSelectFork("rpcAlias", block - 1);
uint256 fork = vm.createSelectFork("mainnet", block - 1);

// block transactions in order: https://beaconcha.in/block/16261704#transactions
// run transactions from current block until tx
Expand Down Expand Up @@ -230,7 +230,7 @@ contract ForkTest is DSTest {
}

function testRpcWithUrl() public {
bytes memory result = vm.rpc("rpcAlias", "eth_blockNumber", "[]");
bytes memory result = vm.rpc("mainnet", "eth_blockNumber", "[]");
uint256 decodedResult = vm.parseUint(vm.toString(result));
assertGt(decodedResult, 20_000_000);
}
Expand Down
17 changes: 4 additions & 13 deletions testdata/default/cheats/RpcUrls.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ contract RpcUrlTest is DSTest {

// returns the correct url
function testCanGetRpcUrl() public {
string memory url = vm.rpcUrl("rpcAlias"); // note: this alias is pre-configured in the test runner
assertEq(url, "https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf");
string memory url = vm.rpcUrl("mainnet");
assertEq(bytes(url).length, 69);
}

// returns an error if env alias does not exist
Expand All @@ -27,21 +27,12 @@ contract RpcUrlTest is DSTest {
);
string[2][] memory _urls = vm.rpcUrls();

string memory url = vm.rpcUrl("rpcAlias");
string memory url = vm.rpcUrl("mainnet");
vm.setEnv("RPC_ENV_ALIAS", url);
string memory envUrl = vm.rpcUrl("rpcEnvAlias");
assertEq(url, envUrl);

string[2][] memory allUrls = vm.rpcUrls();
assertEq(allUrls.length, 3);

string[2] memory val = allUrls[0];
assertEq(val[0], "rpcAlias");

string[2] memory env = allUrls[1];
assertEq(env[0], "rpcAliasSepolia");

string[2] memory env2 = allUrls[2];
assertEq(env2[0], "rpcEnvAlias");
assertGe(allUrls.length, 2);
}
}
2 changes: 1 addition & 1 deletion testdata/default/fork/ForkSame_1.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ contract ForkTest is DSTest {

// this will create two _different_ forks during setup
function setUp() public {
forkA = vm.createFork("https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf", 15_977_624);
forkA = vm.createFork("mainnet", 15_977_624);
}

function testDummy() public {
Expand Down
2 changes: 1 addition & 1 deletion testdata/default/fork/ForkSame_2.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ contract ForkTest is DSTest {

// this will create two _different_ forks during setup
function setUp() public {
forkA = vm.createFork("https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf", 15_977_624);
forkA = vm.createFork("mainnet", 15_977_624);
}

function testDummy() public {
Expand Down
4 changes: 2 additions & 2 deletions testdata/default/fork/Transact.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ contract TransactOnForkTest is DSTest {

function testTransact() public {
// A random block https://etherscan.io/block/17134913
uint256 fork = vm.createFork("rpcAlias", 17134913);
uint256 fork = vm.createFork("mainnet", 17134913);
vm.selectFork(fork);
// a random transfer transaction in the next block: https://etherscan.io/tx/0xaf6201d435b216a858c580e20512a16136916d894aa33260650e164e3238c771
bytes32 tx = 0xaf6201d435b216a858c580e20512a16136916d894aa33260650e164e3238c771;
Expand Down Expand Up @@ -48,7 +48,7 @@ contract TransactOnForkTest is DSTest {

function testTransactCooperatesWithCheatcodes() public {
// A random block https://etherscan.io/block/16260609
uint256 fork = vm.createFork("rpcAlias", 16260609);
uint256 fork = vm.createFork("mainnet", 16260609);
vm.selectFork(fork);

// a random ERC20 USDT transfer transaction in the next block: https://etherscan.io/tx/0x33350512fec589e635865cbdb38fa3a20a2aa160c52611f1783d0ba24ad13c8c
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ contract InvariantRollForkBlockTest is DSTest {
RollForkHandler forkHandler;

function setUp() public {
vm.createSelectFork("rpcAlias", 19812632);
vm.createSelectFork("mainnet", 19812632);
forkHandler = new RollForkHandler();
}

Expand All @@ -39,7 +39,7 @@ contract InvariantRollForkStateTest is DSTest {
RollForkHandler forkHandler;

function setUp() public {
vm.createSelectFork("rpcAlias", 19812632);
vm.createSelectFork("mainnet", 19812632);
forkHandler = new RollForkHandler();
}

Expand Down
Loading

0 comments on commit 0ade1fd

Please sign in to comment.