Skip to content

Commit f744e1a

Browse files
committed
Initial commit
0 parents  commit f744e1a

11 files changed

+294
-0
lines changed

.github/workflows/ci.yml

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ "master" ]
6+
pull_request:
7+
branches: [ "master" ]
8+
9+
env:
10+
CARGO_TERM_COLOR: always
11+
12+
jobs:
13+
build:
14+
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- uses: actions/checkout@v3
19+
- name: Clippy
20+
run: cargo clippy
21+
- name: Check format
22+
run: cargo fmt -- --check
23+
- name: Build
24+
run: cargo build --verbose
25+
- name: Run tests
26+
run: cargo test --verbose

.gitignore

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Generated by Cargo
2+
# will have compiled files and executables
3+
debug/
4+
target/
5+
6+
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7+
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8+
Cargo.lock
9+
10+
# These are backup files generated by rustfmt
11+
**/*.rs.bk
12+
13+
# MSVC Windows builds of rustc generate these, which store debugging information
14+
*.pdb
15+
16+
# RustRover
17+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19+
# and can be added to the global gitignore or merged into this file. For a more nuclear
20+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
21+
#.idea/
22+
23+
# Miscellaneous
24+
msg.tmp
25+
*.tmp
26+
*.secret
27+

CODEOWNERS

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# These owners will be the default owners for everything in
2+
# the repo. Unless a later match takes precedence,
3+
# @jmcph4 will be requested for review when someone opens
4+
# a pull request.
5+
* @jmcph4

Cargo.toml

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
[package]
2+
name = "mkcheckpoint"
3+
version = "0.1.0"
4+
description = "Generate amms-rs-style checkpoints"
5+
edition = "2021"
6+
7+
[dependencies]
8+
amms = { git = "https://github.com/darkforestry/amms-rs", rev = "7d9980a" }
9+
alloy = { git = "https://github.com/alloy-rs/alloy", rev = "dd7a999", features = [
10+
"contract",
11+
"network",
12+
"providers",
13+
"rpc",
14+
"rpc-types",
15+
"rpc-types-eth",
16+
"transports",
17+
"provider-http",
18+
"provider-ws",
19+
"rpc-client",
20+
"pubsub",
21+
"rpc",
22+
"node-bindings",
23+
"transport-ws",
24+
"reqwest",
25+
"serde",
26+
"getrandom",
27+
] }
28+
eyre = "^0.6.0"
29+
csv = "^1.0.0"
30+
clap = { version = "4.5.4", features = ["derive"] }
31+
futures = "0.3.30"
32+
log = "0.4.21"
33+
pretty_env_logger = "0.5.0"
34+
tokio = { version = "^1.0", features = ["full"] }
35+
serde = { version = "1.0.197", features = ["derive"] }
36+
url = "2.5.0"

LICENSE

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
MIT License
2+
3+
Copyright (c) 2024 Jack McPherson
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# mkcheckpoint #
2+
3+
`mkcheckpoint` is a program that generates [`amms-rs`](https://github.com/darkforestry/amms-rs)-style checkpoints from CSV data.
4+

rust-toolchain.toml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[toolchain]
2+
channel = "stable"

rustfmt.toml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
max_width = 80
2+

src/main.rs

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use std::{path::PathBuf, sync::Arc};
2+
3+
use alloy::providers::{Provider, ProviderBuilder};
4+
use amms::amm::factory::Factory;
5+
use amms::{amm::AMM, sync::checkpoint::construct_checkpoint};
6+
use clap::Parser;
7+
use log::{error, info};
8+
use url::Url;
9+
10+
use crate::spec::CheckpointSpecification;
11+
12+
mod spec;
13+
mod variant;
14+
15+
const DEFAULT_RPC_URL: &str = "https://eth.merkle.io";
16+
const DEFAULT_OUTPUT_PATH: &str = ".cfmms-checkpoint.json";
17+
18+
#[derive(Parser)]
19+
struct Opts {
20+
#[clap(short, long)]
21+
rpc: Option<Url>,
22+
r#in: PathBuf,
23+
out: Option<PathBuf>,
24+
}
25+
26+
#[tokio::main]
27+
async fn main() -> eyre::Result<()> {
28+
pretty_env_logger::init_timed();
29+
let opts: Opts = Opts::parse();
30+
31+
let provider = Arc::new(
32+
ProviderBuilder::new().on_http(
33+
opts.rpc.unwrap_or(
34+
DEFAULT_RPC_URL
35+
.parse::<Url>()
36+
.expect("Invalid hardcoded RPC URL"),
37+
),
38+
),
39+
);
40+
41+
/* seems repetitive but minimises network requests! */
42+
let spec = CheckpointSpecification::load(opts.r#in)?;
43+
let factories_and_pools = match spec.fetch(provider.clone()).await {
44+
Ok(t) => {
45+
info!("Retrieved all {} pools", t.len());
46+
t
47+
}
48+
Err(e) => {
49+
error!("Failed to retrieve all pools: {:?}", e);
50+
return Err(e);
51+
}
52+
};
53+
let (factories, pools): (Vec<Factory>, Vec<AMM>) = (
54+
factories_and_pools
55+
.iter()
56+
.map(|(factory, _)| factory)
57+
.cloned()
58+
.collect(),
59+
factories_and_pools
60+
.iter()
61+
.map(|(_, pool)| pool)
62+
.cloned()
63+
.collect(),
64+
);
65+
66+
construct_checkpoint(
67+
factories,
68+
&pools,
69+
provider.get_block_number().await?,
70+
opts.out.unwrap_or(DEFAULT_OUTPUT_PATH.into()),
71+
)?;
72+
Ok(())
73+
}

src/spec.rs

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
use std::{path::Path, sync::Arc};
2+
3+
use alloy::primitives::{Address, BlockNumber};
4+
use alloy::providers::ReqwestProvider;
5+
use amms::amm::{
6+
factory::Factory,
7+
uniswap_v2::{factory::UniswapV2Factory, UniswapV2Pool},
8+
uniswap_v3::{factory::UniswapV3Factory, UniswapV3Pool},
9+
AMM,
10+
};
11+
use csv::Reader;
12+
use futures::future::join_all;
13+
use serde::{Deserialize, Serialize};
14+
15+
use crate::variant::AmmVariant;
16+
17+
pub const DEFAULT_FEE: u32 = 300;
18+
19+
#[derive(Clone, Debug, Deserialize, Serialize)]
20+
pub struct SpecificationEntry {
21+
pub variant: AmmVariant,
22+
pub factory: Address,
23+
pub factory_created: BlockNumber,
24+
pub pool: Address,
25+
}
26+
27+
impl SpecificationEntry {
28+
async fn fetch(
29+
&self,
30+
provider: Arc<ReqwestProvider>,
31+
) -> eyre::Result<(Factory, AMM)> {
32+
Ok(match self.variant {
33+
AmmVariant::UniswapV2 => (
34+
Factory::UniswapV2Factory(UniswapV2Factory::new(
35+
self.factory,
36+
self.factory_created,
37+
DEFAULT_FEE,
38+
)),
39+
AMM::UniswapV2Pool(
40+
UniswapV2Pool::new_from_address(
41+
self.pool,
42+
DEFAULT_FEE,
43+
provider.clone(),
44+
)
45+
.await?,
46+
),
47+
),
48+
AmmVariant::UniswapV3 => (
49+
Factory::UniswapV3Factory(UniswapV3Factory::new(
50+
self.factory,
51+
self.factory_created,
52+
)),
53+
AMM::UniswapV3Pool(
54+
UniswapV3Pool::new_from_address(
55+
self.pool,
56+
DEFAULT_FEE.into(),
57+
provider.clone(),
58+
)
59+
.await?,
60+
),
61+
),
62+
})
63+
}
64+
}
65+
66+
#[derive(Clone, Debug, Deserialize, Serialize)]
67+
pub struct CheckpointSpecification(pub Vec<SpecificationEntry>);
68+
69+
impl CheckpointSpecification {
70+
pub fn load<P>(path: P) -> eyre::Result<Self>
71+
where
72+
P: AsRef<Path>,
73+
{
74+
Ok(Self(
75+
Reader::from_path(path)?
76+
.deserialize()
77+
.collect::<Result<Vec<SpecificationEntry>, csv::Error>>()?,
78+
))
79+
}
80+
81+
pub async fn fetch(
82+
&self,
83+
provider: Arc<ReqwestProvider>,
84+
) -> eyre::Result<Vec<(Factory, AMM)>> {
85+
join_all(self.0.iter().map(|x| x.fetch(provider.clone())))
86+
.await
87+
.into_iter()
88+
.collect::<eyre::Result<Vec<(Factory, AMM)>>>()
89+
}
90+
}

src/variant.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use serde::{Deserialize, Serialize};
2+
3+
#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
4+
pub enum AmmVariant {
5+
UniswapV2,
6+
UniswapV3,
7+
}

0 commit comments

Comments
 (0)