diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..69878d9
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,89 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "generate-readme"
+version = "0.1.0"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+
+[[package]]
+name = "serde"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..6c7a40f
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "generate-readme"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+serde = { version = "1.0.188", default-features = false, features = ["derive"] }
+serde_json = { version = "1.0.106", default-features = false, features = ["alloc"] }
+
+[[bin]]
+name = "generate-readme"
+path = "generate_readme.rs"
diff --git a/TEMPLATE.md b/TEMPLATE.md
new file mode 100644
index 0000000..041aea7
--- /dev/null
+++ b/TEMPLATE.md
@@ -0,0 +1,45 @@
+
+
starknet-snapshots
+
+
+**Starknet full node snapshots maintained by the [zkLend](https://zklend.com) team**
+
+## Downloading the snapshots
+
+There are several ways to download the snapshots. You're recommended to use S3-compatible clients (e.g. `rclone`). It's also possible to just use a plain `curl` command, but it seems to be prone to failures based on reports from users.
+
+### Rclone
+
+Download and install [Rclone](https://rclone.org/). Then, add this section to your config file, typically located at `$HOME/.config/rclone/rclone.conf` (you may need to create the file on a fresh install):
+
+```conf
+[zklend-pathfinder-backup]
+type = s3
+provider = Cloudflare
+region = auto
+endpoint = https://pathfinder-backup.zklend.com/
+```
+
+Then, download a snapshot to the current directory with:
+
+```console
+rclone copy -P zklend-pathfinder-backup:EXAMPLE_NETWORK/EXAMPLE_FILE_NAME .
+```
+
+### `curl`
+
+Downloading with `curl` is not recommended but possible:
+
+```console
+curl -OL "https://pathfinder-backup.zklend.com/EXAMPLE_NETWORK/EXAMPLE_FILE_NAME"
+```
+
+## Using the snapshots
+
+Simply decompress and extract the file downloaded with `tar`:
+
+```console
+tar Jxvf ./EXAMPLE_FILE_NAME
+```
+
+LIST_OF_SNAPSHOTS
diff --git a/generate_readme.rs b/generate_readme.rs
new file mode 100644
index 0000000..f2677b0
--- /dev/null
+++ b/generate_readme.rs
@@ -0,0 +1,138 @@
+use std::{
+ fmt::{Display, Formatter, Write as FmtWrite},
+ io::Write,
+};
+
+use serde::Deserialize;
+
+const JSON: &str = include_str!("./snapshots.json");
+const TEMPLATE: &str = include_str!("./TEMPLATE.md");
+
+#[derive(Deserialize)]
+struct Node {
+ node: NodeType,
+ networks: Vec,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "snake_case")]
+enum NodeType {
+ Pathfinder,
+}
+
+#[derive(Deserialize)]
+struct Network {
+ network: NetworkType,
+ snapshots: Vec,
+}
+
+#[derive(Deserialize)]
+enum NetworkType {
+ #[serde(rename = "mainnet")]
+ Mainnet,
+ #[serde(rename = "goerli-1")]
+ Goerli1,
+ #[serde(rename = "goerli-2")]
+ Goerli2,
+}
+
+#[derive(Deserialize)]
+struct Snapshot {
+ block: u64,
+ version: String,
+}
+
+impl NodeType {
+ fn downloand_link_base(&self) -> &str {
+ match self {
+ Self::Pathfinder => "https://pathfinder-backup.zklend.com/",
+ }
+ }
+}
+
+impl Snapshot {
+ fn file_name(&self, network: &NetworkType) -> String {
+ format!("{}-v{}-{}.tar.xz", network, self.version, self.block)
+ }
+}
+
+impl Display for NodeType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Pathfinder => write!(f, "pathfinder"),
+ }
+ }
+}
+
+impl Display for NetworkType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Mainnet => write!(f, "mainnet"),
+ Self::Goerli1 => write!(f, "goerli-1"),
+ Self::Goerli2 => write!(f, "goerli-2"),
+ }
+ }
+}
+
+fn main() {
+ let nodes: Vec = serde_json::from_str(JSON).expect("unable to deserialize JSON");
+
+ let mut snapshot_list = String::new();
+
+ let mut example_network: Option = None;
+ let mut example_file_name: Option = None;
+
+ for node in nodes.iter() {
+ writeln!(snapshot_list, "## `{}`", node.node).unwrap();
+
+ for network in node.networks.iter() {
+ writeln!(snapshot_list).unwrap();
+ writeln!(snapshot_list, "### `{}`", network.network).unwrap();
+ writeln!(snapshot_list).unwrap();
+
+ writeln!(
+ snapshot_list,
+ "| Network | Version | Block | Download Link |"
+ )
+ .unwrap();
+ writeln!(snapshot_list, "| - | - | - | - |").unwrap();
+
+ for snapshot in network.snapshots.iter() {
+ let network_name = format!("{}", network.network);
+ let file_name = snapshot.file_name(&network.network);
+
+ if example_network.is_none() {
+ example_network = Some(network_name.clone());
+ }
+ if example_file_name.is_none() {
+ example_file_name = Some(file_name.clone());
+ }
+
+ writeln!(
+ snapshot_list,
+ "| `{}` | `v{}` | `{}` | {}{}/{} |",
+ network_name,
+ snapshot.version,
+ snapshot.block,
+ node.node.downloand_link_base(),
+ network_name,
+ file_name
+ )
+ .unwrap();
+ }
+ }
+ }
+
+ let example_network = example_network.expect("no example network generated");
+ let example_file_name = example_file_name.expect("no example file name generated");
+
+ let full_readme = TEMPLATE
+ .replace("EXAMPLE_NETWORK", &example_network)
+ .replace("EXAMPLE_FILE_NAME", &example_file_name)
+ .replace("LIST_OF_SNAPSHOTS", &snapshot_list);
+
+ let mut file = std::fs::File::create("./README.md").expect("unable to create file");
+
+ file.write_all(full_readme.as_bytes())
+ .expect("unable to write to file");
+}
diff --git a/snapshots.json b/snapshots.json
new file mode 100644
index 0000000..a3741c8
--- /dev/null
+++ b/snapshots.json
@@ -0,0 +1,46 @@
+[
+ {
+ "node": "pathfinder",
+ "networks": [
+ {
+ "network": "mainnet",
+ "snapshots": [
+ {
+ "block": 213169,
+ "version": "0.8.1"
+ },
+ {
+ "block": 141083,
+ "version": "0.7.0"
+ }
+ ]
+ },
+ {
+ "network": "goerli-1",
+ "snapshots": [
+ {
+ "block": 860674,
+ "version": "0.8.1"
+ },
+ {
+ "block": 841817,
+ "version": "0.7.0"
+ }
+ ]
+ },
+ {
+ "network": "goerli-2",
+ "snapshots": [
+ {
+ "block": 141801,
+ "version": "0.8.1"
+ },
+ {
+ "block": 136651,
+ "version": "0.7.0"
+ }
+ ]
+ }
+ ]
+ }
+]