ci: mdbook (#2003)

* Applied same book structure as Reth

* ci: [WIP] added book workflow

* fix warning mdbook version different from template plugin version

* ci: added deploy step

* Http provider docs

* IPC provider docs

* RetryClient docs

* WebSocket provider docs

Co-authored-by: Andrea Simeoni <>
This commit is contained in:
Andrea Simeoni 2023-01-04 11:36:31 +01:00 committed by GitHub
parent 4fd742f8ce
commit da0363918b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 309 additions and 41 deletions

61
.github/workflows/book.yml vendored Normal file
View File

@ -0,0 +1,61 @@
name: book
on:
push:
branches: [master]
paths:
- 'book/**'
- 'book.toml'
pull_request:
branches: [master]
paths:
- 'book/**'
- 'book.toml'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install mdbook
run: |
mkdir mdbook
curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.21/mdbook-v0.4.21-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
echo `pwd`/mdbook >> $GITHUB_PATH
- name: Install mdbook-template
run: |
mkdir mdbook-template
curl -sSL https://github.com/sgoudham/mdbook-template/releases/latest/download/mdbook-template-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook-template
echo `pwd`/mdbook-template >> $GITHUB_PATH
- name: Build
run: mdbook build
- name: Save pages artifact
uses: actions/upload-pages-artifact@v1
with:
path: target/book
deploy:
# Only deploy if a push to master
if: github.ref_name == 'master' && github.event_name == 'push'
runs-on: ubuntu-latest
needs: [build]
# Grant GITHUB_TOKEN the permissions required to make a Pages deployment
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
# reference: https://github.com/actions/deploy-pages
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1

1
Cargo.lock generated
View File

@ -1684,6 +1684,7 @@ version = "1.0.2"
dependencies = [ dependencies = [
"ethers", "ethers",
"eyre", "eyre",
"reqwest",
"serde", "serde",
"serde_json", "serde_json",
"tokio", "tokio",

27
book.toml Normal file
View File

@ -0,0 +1,27 @@
[book]
authors = ["The ethers-rs contributors"]
language = "en"
multilingual = false
src = "book"
title = "Ethers.rs: The Ethereum Library for Rust"
description = "A book on all things ethers-rs"
[output.html]
git-repository-url = "https://github.com/gakonst/ethers-rs"
default-theme = "ayu"
no-section-label = true
[output.html.fold]
enable = true
level = 1
[build]
build-dir = "target/book"
[preprocessor.template]
before = [ "links" ]
[preprocessor.index]
[preprocessor.links]

View File

@ -7,13 +7,13 @@
# Reference guide # Reference guide
- [Providers]() - [Providers]()
- [Http]() - [Http](./providers/http.md)
- [IPC]() - [IPC](./providers/ipc.md)
- [Mock]() - [Mock]()
- [Quorum]() - [Quorum]()
- [Retry]() - [Retry](./providers/retry.md)
- [RW]() - [RW]()
- [WS]() - [WebSocket](./providers/ws.md)
- [Middleware]() - [Middleware]()
- [Builder]() - [Builder]()
- [Create custom middleware]() - [Create custom middleware]()

View File

@ -0,0 +1,6 @@
# Comparison and equivalence
```rust
{{#include ../../examples/big-numbers/examples/comparison_equivalence.rs}}
```

View File

@ -0,0 +1,5 @@
# Conversion
```rust
{{#include ../../examples/big-numbers/examples/conversion.rs}}
```

View File

@ -0,0 +1,5 @@
# Creating instances
```rust
{{#include ../../examples/big-numbers/examples/create_instances.rs}}
```

View File

@ -0,0 +1 @@
{{#include ../../examples/big-numbers/README.md}}

View File

@ -0,0 +1,5 @@
# Math operations
```rust
{{#include ../../examples/big-numbers/examples/math_operations.rs}}
```

View File

@ -0,0 +1,5 @@
# Utilities
```rust
{{#include ../../examples/big-numbers/examples/utilities.rs}}
```

5
book/providers/http.md Normal file
View File

@ -0,0 +1,5 @@
# Http provider
```rust
{{#include ../../examples/providers/examples/http.rs}}
```

5
book/providers/ipc.md Normal file
View File

@ -0,0 +1,5 @@
# IPC provider
```rust
{{#include ../../examples/providers/examples/ipc.rs}}
```

5
book/providers/retry.md Normal file
View File

@ -0,0 +1,5 @@
# Retry client
```rust
{{#include ../../examples/providers/examples/retry.rs}}
```

5
book/providers/ws.md Normal file
View File

@ -0,0 +1,5 @@
# WebSocket provider
```rust
{{#include ../../examples/providers/examples/ws.rs}}
```

View File

@ -1 +0,0 @@
book

View File

@ -1,6 +0,0 @@
[book]
authors = ["The ethers-rs contributors"]
language = "en"
multilingual = false
src = "src"
title = "Ethers.rs: The Ethereum Library for Rust"

View File

@ -1,6 +0,0 @@
# Comparison and equivalence
```rust
{{#include ../../../../examples/big-numbers/examples/comparison_equivalence.rs}}
```

View File

@ -1,5 +0,0 @@
# Conversion
```rust
{{#include ../../../../examples/big-numbers/examples/conversion.rs}}
```

View File

@ -1,5 +0,0 @@
# Creating instances
```rust
{{#include ../../../../examples/big-numbers/examples/create_instances.rs}}
```

View File

@ -1 +0,0 @@
{{#include ../../../../examples/big-numbers/README.md}}

View File

@ -1,5 +0,0 @@
# Math operations
```rust
{{#include ../../../../examples/big-numbers/examples/math_operations.rs}}
```

View File

@ -1,5 +0,0 @@
# Utilities
```rust
{{#include ../../../../examples/big-numbers/examples/utilities.rs}}
```

View File

@ -35,13 +35,13 @@
- [ ] Time lag - [ ] Time lag
- [ ] Transformer - [ ] Transformer
- [ ] Providers - [ ] Providers
- [ ] Http - [x] Http
- [x] IPC - [x] IPC
- [ ] Mock - [ ] Mock
- [x] Quorum - [x] Quorum
- [ ] Retry - [x] Retry
- [x] RW - [x] RW
- [ ] WS - [x] WS
- [ ] Queries - [ ] Queries
- [ ] Blocks - [ ] Blocks
- [x] Contracts - [x] Contracts

View File

@ -12,6 +12,7 @@ ipc = []
ethers = { path = "../..", version = "1.0.0", features = ["abigen", "ipc", "rustls", "ws"] } ethers = { path = "../..", version = "1.0.0", features = ["abigen", "ipc", "rustls", "ws"] }
eyre = "0.6" eyre = "0.6"
reqwest = { version = "0.11.13", default-features = false }
serde = { version = "1.0.144", features = ["derive"] } serde = { version = "1.0.144", features = ["derive"] }
serde_json = "1.0.64" serde_json = "1.0.64"
tokio = { version = "1.18", features = ["macros"] } tokio = { version = "1.18", features = ["macros"] }

View File

@ -0,0 +1,67 @@
use ethers::prelude::*;
use reqwest::header::{HeaderMap, HeaderValue};
use std::sync::Arc;
const RPC_URL: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27";
/// The Http transport is used to send JSON-RPC requests over Http to an
/// Ethereum node. It allows you to perform various actions on the Ethereum blockchain, such as
/// reading and writing data, sending transactions, and more. To use the Http transport, you will
/// need to create a new `Provider` instance as described in this example.
#[tokio::main]
async fn main() -> eyre::Result<()> {
create_instance().await?;
share_providers_across_tasks().await?;
Ok(())
}
async fn create_instance() -> eyre::Result<()> {
// An Http provider can be created from an http(s) URI.
// In case of https you must add the "rustls" or "openssl" feature
// to the ethers library dependency in `Cargo.toml`.
let _provider = Provider::<Http>::try_from(RPC_URL)?;
// Instantiate with auth to append basic authorization headers across requests
let url = reqwest::Url::parse(RPC_URL)?;
let auth = Authorization::basic("username", "password");
let _provider = Http::new_with_auth(url, auth)?;
// Instantiate from custom Http Client if you need
// finer control over the Http client configuration
// (TLS, Proxy, Cookies, Headers, etc.)
let url = reqwest::Url::parse(RPC_URL)?;
let mut headers = HeaderMap::new();
headers.insert("Authorization", HeaderValue::from_static("Bearer my token"));
headers.insert("X-MY-HEADERS", HeaderValue::from_static("Some value"));
let http_client = reqwest::Client::builder()
.default_headers(headers)
.proxy(reqwest::Proxy::all("http://proxy.example.com:8080")?)
.build()?;
let _provider = Http::new_with_client(url, http_client);
Ok(())
}
/// Providers can be easily shared across tasks using `Arc` smart pointers
async fn share_providers_across_tasks() -> eyre::Result<()> {
let provider: Provider<Http> = Provider::<Http>::try_from(RPC_URL)?;
let client_1 = Arc::new(provider);
let client_2 = Arc::clone(&client_1);
let handle1 =
tokio::spawn(async move { client_1.get_block(BlockNumber::Latest).await.unwrap_or(None) });
let handle2 =
tokio::spawn(async move { client_2.get_block(BlockNumber::Latest).await.unwrap_or(None) });
let block1: Option<Block<H256>> = handle1.await?;
let block2: Option<Block<H256>> = handle2.await?;
println!("{block1:?} {block2:?}");
Ok(())
}

View File

@ -1,11 +1,23 @@
/// The IPC (Inter-Process Communication) transport is a way for a process to communicate with a
/// running Ethereum client over a local Unix domain socket. Using the IPC transport allows the
/// ethers library to send JSON-RPC requests to the Ethereum client and receive responses, without
/// the need for a network connection or HTTP server. This can be useful for interacting with a
/// local Ethereum node that is running on the same machine.
#[tokio::main] #[tokio::main]
#[cfg(feature = "ipc")] #[cfg(feature = "ipc")]
async fn main() -> eyre::Result<()> { async fn main() -> eyre::Result<()> {
use ethers::prelude::*; use ethers::prelude::*;
// We instantiate the provider using the path of a local Unix domain socket
// --------------------------------------------------------------------------------
// NOTE: The IPC transport supports push notifications, but we still need to specify a polling
// interval because only subscribe RPC calls (e.g., transactions, blocks, events) support push
// notifications in Ethereum's RPC API. For other calls we must use repeated polling for many
// operations even with the IPC transport.
let provider = Provider::connect_ipc("~/.ethereum/geth.ipc") let provider = Provider::connect_ipc("~/.ethereum/geth.ipc")
.await? .await?
.interval(std::time::Duration::from_millis(2000)); .interval(std::time::Duration::from_millis(2000));
let block = provider.get_block_number().await?; let block = provider.get_block_number().await?;
println!("Current block: {block}"); println!("Current block: {block}");
let mut stream = provider.watch_blocks().await?.stream(); let mut stream = provider.watch_blocks().await?.stream();

View File

@ -0,0 +1,35 @@
use ethers::prelude::*;
use reqwest::Url;
use std::time::Duration;
const RPC_URL: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27";
/// The RetryClient is a type that wraps around a JsonRpcClient and automatically retries failed
/// requests using an exponential backoff and filtering based on a RetryPolicy. It presents as a
/// JsonRpcClient, but with additional functionality for retrying requests.
///
/// The RetryPolicy can be customized for specific applications and endpoints, mainly to handle
/// rate-limiting errors. In addition to the RetryPolicy, errors caused by connectivity issues such
/// as timed out connections or responses in the 5xx range can also be retried separately.
#[tokio::main]
async fn main() -> eyre::Result<()> {
let provider = Http::new(Url::parse(RPC_URL)?);
let client = RetryClientBuilder::default()
.rate_limit_retries(10)
.timeout_retries(3)
.initial_backoff(Duration::from_millis(500))
.build(provider, Box::new(HttpRateLimitRetryPolicy::default()));
// Send a JSON-RPC request for the latest block
let block_num = "latest".to_string();
let txn_details = false;
let params = (block_num, txn_details);
let block: Block<H256> =
JsonRpcClient::request(&client, "eth_getBlockByNumber", params).await?;
println!("{block:?}");
Ok(())
}

View File

@ -0,0 +1,51 @@
use std::time::Duration;
use ethers::prelude::*;
const WSS_URL: &str = "wss://mainnet.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27";
type BoxErr = Box<dyn std::error::Error>;
/// The Ws transport allows you to send JSON-RPC requests and receive responses over WebSocket
/// connections. It is useful for connecting to Ethereum nodes that support WebSockets.
/// This allows to interact with the Ethereum network in real-time without the need for HTTP
/// polling.
#[tokio::main]
async fn main() -> Result<(), BoxErr> {
create_instance().await?;
watch_blocks().await?;
Ok(())
}
async fn create_instance() -> Result<(), BoxErr> {
// An Ws provider can be created from an ws(s) URI.
// In case of wss you must add the "rustls" or "openssl" feature
// to the ethers library dependency in `Cargo.toml`.
//------------------------------------------------------------------------------------------
// NOTE: The Ws transport supports push notifications, but we still need to specify a polling
// interval because only subscribe RPC calls (e.g., transactions, blocks, events) support push
// notifications in Ethereum's RPC API. For other calls we must use repeated polling for many
// operations even with the Ws transport.
let _provider = Provider::<Ws>::connect(WSS_URL).await?.interval(Duration::from_millis(500));
// Instantiate with auth to send basic authorization headers on connection.
let url = reqwest::Url::parse(WSS_URL)?;
let auth = Authorization::basic("username", "password");
if let Ok(_provider) = Provider::<Ws>::connect_with_auth(url, auth).await {
println!("Create Ws provider with auth");
}
Ok(())
}
/// Let's show how the Ws connection enables listening for blocks using a persistent TCP connection
async fn watch_blocks() -> Result<(), BoxErr> {
let provider = Provider::<Ws>::connect(WSS_URL).await?;
let mut stream = provider.watch_blocks().await?.take(1);
while let Some(block_hash) = stream.next().await {
println!("{block_hash:?}");
}
Ok(())
}