diff --git a/.github/workflows/book.yaml b/.github/workflows/book.yaml index d0e54a82..1e9eac30 100644 --- a/.github/workflows/book.yaml +++ b/.github/workflows/book.yaml @@ -12,73 +12,69 @@ on: merge_group: jobs: - test: + build: runs-on: ubuntu-latest - name: test steps: - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + # Install Rust + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + # Install mdbook - name: Install mdbook - run: | - mkdir mdbook - curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.14/mdbook-v0.4.14-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook - echo `pwd`/mdbook >> $GITHUB_PATH + run: cargo install mdbook --vers "^0.4.35" - - 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 + # Install mdbook-katex + - name: Install mdbook-katex + run: cargo install mdbook-katex - - name: Run tests - run: mdbook test + # Build book + - name: build book + run: mdbook build lint: runs-on: ubuntu-latest - name: lint steps: - uses: actions/checkout@v3 - + with: + fetch-depth: 0 + + # Install Rust + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + # Install mdbook-linkcheck - name: Install mdbook-linkcheck - run: | - mkdir mdbook-linkcheck - curl -sSL -o mdbook-linkcheck.zip https://github.com/Michael-F-Bryan/mdbook-linkcheck/releases/latest/download/mdbook-linkcheck.x86_64-unknown-linux-gnu.zip - unzip mdbook-linkcheck.zip -d ./mdbook-linkcheck - chmod +x `pwd`/mdbook-linkcheck/mdbook-linkcheck - echo `pwd`/mdbook-linkcheck >> $GITHUB_PATH - - - name: Run linkcheck + run: cargo install mdbook-linkcheck + + - name: linkcheck run: mdbook-linkcheck --standalone - build: + test: runs-on: ubuntu-latest + steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - - - name: Install toolchain - uses: dtolnay/rust-toolchain@nightly - - uses: Swatinem/rust-cache@v2 + + # Install Rust + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 with: - cache-on-failure: true - - - name: Install mdbook - run: | - mkdir mdbook - curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.14/mdbook-v0.4.14-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 book - run: mdbook build documentation - - - name: Build docs - run: RUSTDOCFLAGS="--enable-index-page -Zunstable-options" cargo +nightly doc --all --no-deps \ No newline at end of file + toolchain: stable + override: true + + - name: test + run: cargo test --package documentation \ No newline at end of file diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index f562322b..12237c82 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -20,7 +20,7 @@ jobs: override: true - name: test - run: cargo test --workspace --all-features + run: cargo test --workspace --all-features --exclude documentation codecov: name: codecov @@ -38,7 +38,7 @@ jobs: uses: taiki-e/install-action@cargo-llvm-cov - name: codecov - run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info + run: cargo llvm-cov --all-features --workspace --exclude documentation --lcov --output-path lcov.info - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v3 diff --git a/.gitignore b/.gitignore index e5d62e4a..7baa747c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,6 @@ arbiter/ book arbiter-core/data -example_fork/test.json \ No newline at end of file +example_fork/test.json + +doctest_cache/ \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 8f1f1783..77de4b6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -273,7 +273,7 @@ dependencies = [ "async-stream", "async-trait", "bytes", - "cargo_metadata", + "cargo_metadata 0.18.1", "chrono", "crossbeam-channel", "ethers", @@ -794,6 +794,12 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +[[package]] +name = "bytecount" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" + [[package]] name = "bytemuck" version = "1.14.0" @@ -883,6 +889,19 @@ dependencies = [ "serde", ] +[[package]] +name = "cargo_metadata" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.20", + "serde", + "serde_json", +] + [[package]] name = "cargo_metadata" version = "0.18.1" @@ -1389,6 +1408,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "documentation" +version = "0.0.0" +dependencies = [ + "arbiter-bindings", + "arbiter-core", + "skeptic", + "tokio", +] + [[package]] name = "dunce" version = "1.0.4" @@ -1524,6 +1553,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "version_check", +] + [[package]] name = "eth-keystore" version = "0.5.0" @@ -1689,7 +1727,7 @@ checksum = "918b1a9ba585ea61022647def2f27c29ba19f6d2a4a4c8f68a9ae97fd5769737" dependencies = [ "arrayvec", "bytes", - "cargo_metadata", + "cargo_metadata 0.18.1", "chrono", "const-hex", "elliptic-curve", @@ -4004,6 +4042,17 @@ dependencies = [ "unarray", ] +[[package]] +name = "pulldown-cmark" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" +dependencies = [ + "bitflags 1.3.2", + "memchr", + "unicase", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -4881,6 +4930,21 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "skeptic" +version = "0.13.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8" +dependencies = [ + "bytecount", + "cargo_metadata 0.14.2", + "error-chain", + "glob", + "pulldown-cmark", + "tempfile", + "walkdir", +] + [[package]] name = "slab" version = "0.4.9" @@ -5638,6 +5702,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.15" diff --git a/Cargo.toml b/Cargo.toml index e5283640..ec37abee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] # List of crates included in this workspace -members = [ "arbiter-bindings", "arbiter-core", "arbiter-engine"] +members = [ "arbiter-bindings", "arbiter-core", "arbiter-engine", "documentation"] # List of crates excluded from this workspace exclude = ["benches"] diff --git a/arbiter-core/src/environment/fork.rs b/arbiter-core/src/environment/fork.rs index cdcbc221..f4aa360e 100644 --- a/arbiter-core/src/environment/fork.rs +++ b/arbiter-core/src/environment/fork.rs @@ -47,7 +47,7 @@ impl Fork { // Read the file let mut cwd = env::current_dir().unwrap(); cwd.push(path); - info!("Reading db from: {:?}", cwd); + print!("Reading db from: {:?}", cwd); let data = fs::read_to_string(cwd).unwrap(); // Deserialize the JSON data to your OutputData type diff --git a/arbiter-core/src/environment/instruction.rs b/arbiter-core/src/environment/instruction.rs index 0adab195..ed268ce2 100644 --- a/arbiter-core/src/environment/instruction.rs +++ b/arbiter-core/src/environment/instruction.rs @@ -154,6 +154,7 @@ pub(crate) enum EnvironmentData { /// The query is for the balance of an account given by the inner `Address`. Balance(ethers::types::Address), + // TODO: Rename this to `Nonce`? /// The query is for the nonce of an account given by the inner `Address`. TransactionCount(ethers::types::Address), } diff --git a/book.toml b/book.toml index ed0a550b..c165d4b1 100644 --- a/book.toml +++ b/book.toml @@ -27,6 +27,11 @@ include-src = false block-delimiter = { left = "$$", right = "$$" } inline-delimiter = { left = "$", right = "$" } +[preprocessor.keeper] +command = "mdbook-keeper" +manifest_dir = "./documentation/" +externs = ["arbiter_core", "arbiter_bindings", "tokio"] + [output.linkcheck] optional = true follow-web-links = true \ No newline at end of file diff --git a/documentation/Cargo.toml b/documentation/Cargo.toml new file mode 100644 index 00000000..f619f258 --- /dev/null +++ b/documentation/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "documentation" +version = "0.0.0" +edition = "2021" +build = "build.rs" + +[build-dependencies] +skeptic = "0.13.7" +tokio.workspace = true + +[dev-dependencies] +skeptic = "0.13.7" +arbiter-core.workspace = true +arbiter-bindings.workspace = true + diff --git a/documentation/build.rs b/documentation/build.rs new file mode 100644 index 00000000..104d80e5 --- /dev/null +++ b/documentation/build.rs @@ -0,0 +1,8 @@ +use skeptic::markdown_files_of_directory; + +extern crate skeptic; + +fn main() { + let markdown_files = markdown_files_of_directory("src/"); + skeptic::generate_doc_tests(&markdown_files); +} diff --git a/documentation/src/SUMMARY.md b/documentation/src/SUMMARY.md index 9c592cd4..5dcf5d82 100644 --- a/documentation/src/SUMMARY.md +++ b/documentation/src/SUMMARY.md @@ -1,13 +1,22 @@ # Summary - +[Arbiter](./index.md) - [Introduction](./introduction/introduction.md) - [Getting Started / Installation](./getting_started/getting_started.md) - [Setting up Simulations](./getting_started/setting_up_simulations.md) + +# Usage +- [Overview](./usage/index.md) + - [Arbiter CLI](./usage/arbiter_cli.md) + - [Arbiter Core](./usage/arbiter_core.md) + - [Arbiter Engine](./usage/arbiter_engine.md) + +# Methodology - [Anomaly Detection](./introduction/anomaly_detection.md) - [Auditing](./auditing/auditing.md) - [Risk](./risk/risk_metrics.md) - [Metrics](./risk/metrics.md) +# Engagement - [Contributing](./contributing/contributing.md) - [Project Architecture](./contributing/architecture.md) - [Vulnerability Corpus](./contributing/vulnerability_corpus.md) diff --git a/documentation/src/getting_started/getting_started.md b/documentation/src/getting_started/getting_started.md index 15dfcb37..afef0268 100644 --- a/documentation/src/getting_started/getting_started.md +++ b/documentation/src/getting_started/getting_started.md @@ -37,8 +37,17 @@ To create a new Arbiter project: arbiter init your-new-project cd your-new-project ``` +This initializes a new Arbiter project with a template. +You can run `arbiter init --no-git` to remove the `.git` directory from the template upon initialization. -This initializes a new Arbiter project with a template. You can generate the bindings again by running: +### Bindings + +You can load or write your own smart contracts in the `arbiter-bindings/contracts/` directory and begin writing your own simulations. +Arbiter treats Rust smart-contract bindings as first-class citizens. +The contract bindings are generated via Foundry's `forge` command. +`arbiter bind` wraps `forge` with some convenience features that will generate all your bindings to src/bindings as a rust module. +[Foundry](https://github.com/foundry-rs/foundry) power-users are welcome to use `forge` directly. +You can generate the bindings again by running: ```bash arbiter bind @@ -57,4 +66,23 @@ The template is executable at this point and you can run it by running: cargo run ``` -You can load or write your own smart contracts in the templates `contracts/` directory and begin writing your own simulations. Arbiter treats Rust smart-contract bindings as first-class citizens. The contract bindings are generated via Foundry's forge command. arbiter bind wraps forge with some convenience features that will generate all your bindings to `src/bindings` as a rust module. Foundry power-users are welcome to use forge directly. You can also manage project dependencies using git submodules via `forge install`. The [Foundry book](https://book.getfoundry.sh/reference/forge/forge-install) provides further details on managing project dependencies and other features. \ No newline at end of file +You can load or write your own smart contracts in the templates `contracts/` directory and begin writing your own simulations. Arbiter treats Rust smart-contract bindings as first-class citizens. The contract bindings are generated via Foundry's forge command. arbiter bind wraps forge with some convenience features that will generate all your bindings to `src/bindings` as a rust module. Foundry power-users are welcome to use forge directly. You can also manage project dependencies using git submodules via `forge install`. The [Foundry book](https://book.getfoundry.sh/reference/forge/forge-install) provides further details on managing project dependencies and other features. + + +### Forking + +To fork a state of an EVM network, you must first create a fork config file. +An example is provided in the `example_fork` directory. +Essentially, you provide your storage location for the data, the network you want the block number you want, and metadata about the contracts you want to fork. + +```bash +arbiter fork +``` + +This will create a fork of the network you specified in the config file and store it in the location you specified. +It can then be loaded into an `arbiter-core` `Environment` by using the `Fork::from_disk()` method. + +Forking is done this way to make sure that all emulation done does not require a constant connection to an RPC-endpoint. + +**Optional Arguments** +You can run `arbiter fork --overwrite` to overwrite the fork if it already exists. \ No newline at end of file diff --git a/documentation/src/index.md b/documentation/src/index.md new file mode 100644 index 00000000..90c0a6df --- /dev/null +++ b/documentation/src/index.md @@ -0,0 +1,50 @@ +# Arbiter +**Arbiter** is a framework for stateful Ethereum smart-contract simulation. +The framework features an [`ethers-rs`](https://github.com/gakonst/ethers-rs) middleware built on top of [revm](https://github.com/bluealloy/revm) which allows the end user to interact with a sandboxed `revm` instance as if it were an Ethereum node. +This provides a familiar interface for interacting with the Ethereum Virtual Machine (EVM), but with unrivaled speed. +Furthermore, Arbiter provides containment and management for simulations. For a running list of vulnerabilities found with Arbiter, please see the [Vulnerability Corpus](contributing/vulnerability_corpus.md). + +## Overview +The Arbiter workspace has three crates: +- `arbiter`: The binary crate that exposes a command line interface for initializing simulations via a templated repository and generating contract bindings needed for the simulation. +- `arbiter-core`: The lib crate that contains the core logic for the Arbiter framework including the `RevmMiddleware` discussed before, the `Environment` which envelopes simulations, and the `Manager` who controls a collection of environments. +- `arbiter-engine`: The lib crate that provides abstractions for building simulations and more. + +The purpose of Arbiter is to provide a toolset to construct arbitrary agents (defined in Rust, by smart contracts, or even other foreign function interfaces) and have these agents interact with an Ethereum-like environment of your design. +All contract bytecode is run directly using a blazing-fast EVM instance `revm` (which is used in live RPC nodes such as [`reth`](https://github.com/paradigmxyz/reth)) so that your contracts are tested in the exact same type of environment that they are deployed in. + +## Motivation +Smart contract engineers need to test their contracts against a wide array of potentially adversarial environments and contract parameters. +The static stateless testing of contracts can only take you so far. +To truly test the security of a contract, you need to test it against a wide array of dynamic environments that encompass the externalities of Ethereum mainnet. +We wanted to do just that with Arbiter. + +Both smart contract and financial engineers come together in Decentralized Finance (DeFi) to build and deploy a wide array of complex decentralized applications as well as financial strategies respectively. +For the latter, a financial engineer may want to test their strategies against thousands of market conditions, contract settings, shocks, and autonomous or random or even AI agents all while making sure their strategy isn't vulnerable to bytecode-level exploits. + +To configure such a rich simulation environment on a test or local network is also possible with Arbiter by a change in choice of middleware. +The most efficient choice for getting robust, yet quick, simulations would bypass any networking and use a low level language's implementation of the EVM. +Furthermore, we can gain control over the EVM worldstate by working directly on `revm`. +We would like the user to have a choice in how they want to simulate their contracts and Arbiter provides that choice. + +### Sim Driven Development and Strategization + +Test driven development is a popular engineering practice to write tests first, which fail, and implement logic to get the test to eventually pass. +With simulation driven development, it's possible to build "tests" that can only pass if the *incentives* actually work. For example, a sim driven test might be `is_loan_liquidated`, and a simulation must be made for a liquidator agent to do the liquidation. +This approach significantly improves the testing of economic systems and other mechanism designs, which is important in the world of networks that are mostly incentive driven. + +The same goes with developing strategies that one would like to deploy on a live Ethereum network. +One can use Arbiter to simulate their strategy with an intended goal and see if it actually works. +This is especially important in the world of DeFi where strategies are often a mix of on and offchain and are susceptible to exploits. + +## Developer Documentation +To see the documentation for the Arbiter crates, please visit the following: +- [`arbiter`](https://docs.rs/crate/arbiter/) +- [`arbiter-bindings`](https://docs.rs/crate/arbiter-bindings/) +- [`arbiter-core`](https://docs.rs/arbiter-core/) + +You will also find each of these on crates.io. + +## Contributing + +See our [Contributing Guidelines](https://github.com/primitivefinance/arbiter/blob/main/.github/CONTRIBUTING.md) diff --git a/documentation/src/lib.rs b/documentation/src/lib.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/documentation/src/lib.rs @@ -0,0 +1 @@ + diff --git a/documentation/src/usage/arbiter_cli.md b/documentation/src/usage/arbiter_cli.md new file mode 100644 index 00000000..42284478 --- /dev/null +++ b/documentation/src/usage/arbiter_cli.md @@ -0,0 +1 @@ +# Arbiter CLI diff --git a/documentation/src/usage/arbiter_core.md b/documentation/src/usage/arbiter_core.md new file mode 100644 index 00000000..362e432b --- /dev/null +++ b/documentation/src/usage/arbiter_core.md @@ -0,0 +1,111 @@ +# Arbiter Core +The `arbiter-core` crate is the core of the Arbiter framework. +It contains the `Environment` struct which acts as an EVM sandbox and the `RevmMiddleware` which gives a convenient interface for interacting with contracts deployed into the `Environment`. +The API provided by `RevmMiddleware` is that of the `Middleware` trait in the `ethers-rs` crate, therefore it looks and feels just like you're interacting with a live network when you work with an Arbiter `Environment`. +The only notable differences are in the control you have over this `Environment` compared to something like Anvil, a testnet, or a live network. + +## Environment +The `Environment` owns a `revm` instance for processing EVM bytecode. +To make the `Environment` performant and flexible, it runs on its own system thread and receives all communication via `Instruction`s sent to it via a `Sender`. +The `Socket` is a struct owned by the `Environment` that manages all inward and outward communication with the `Environment`'s clients, such as the `Instruction` channel. + +### Usage +To create an `Environment`, we use a builder pattern that allows you to pre-load an `Environment` with your own database. +We can do the following to create a default `Environment`: +```rust +use arbiter_core::environment::builder::EnvironmentBuilder; + +fn main() { + let env = EnvironmentBuilder::new().build(); +} +``` +Note that the call to `.build()` will start the `Environment`'s thread and begin processing `Instruction`s. + +If you have a database that has been forked from a live network, it has likely been serialized to disk. +In which case, you can do something like this: +```rust, ignore +use arbiter_core::environment::builder::EnvironmentBuilder; +use arbiter_core::environment::fork::Fork; + +fn main() { + let path_to_fork = "path/to/fork"; + let fork = Fork::from_disk(path_to_fork).unwrap(); + let env = EnvironmentBuilder::new().db(fork).build(); +} +``` +This will create an `Environment` that has been forked from the database at the given path and is ready to receive `Instruction`s. + +### Instructions +`Instruction`s have been added to over time, but at the moment we allow for the following: +- `Instruction::AddAccount`: Add an account to the `Environment`'s world state. This is usually called by the `RevmMiddleware` when a new client is created. +- `Instruction::BlockUpdate`: Update the `Environment`'s block number and block timestamp. This can be handled by an external agent in a simulation, if desired. +- `Instruction::Cheatcode`: Execute one of the `Cheatcodes` on the `Environment`'s world state. +The `Cheatcodes` include: + - `Cheatcodes::Deal`: Used to set the raw ETH balance of a user. Useful when you need to pay gas fees in a transaction. + - `Cheatcodes::Load`: Gets the value of a storage slot of an account. + - `Cheatcodes::Store`: Sets the value of a storage slot of an account. + - `Cheatcodes::Access`: Gets the account at an address. +- `Instruction::Query`: Allows for querying the `Environment`'s world state and current configuration. Anything in the `EnvironmentData` enum is accessible via this instruction. + - `EnvironmentData::BlockNumber`: Gets the current block number of the `Environment`. + - `EnvironmentData::BlockTimestamp`: Gets the current block timestamp of the `Environment`. + - `EnvironmentData::GasPrice`: Gets the current gas price of the `Environment`. + - `EnvironmentData::Balance`: Gets the current ETH balance of an account. + - `EnvironmentData::TransactionCount`: Gets the current nonce of an account. +- `Instruction::Stop`: Stops the `Environment`'s thread and echos out to any listeners to shut down their event streams. This can be used when handling errors or reverts, or just when you're done with the `Environment`. +- `Instruction::Transaction`: Executes a transaction on the `Environment`'s world state. This is usually called by the `RevmMiddleware` when a client sends a ETH-call or state-changing transaction. + +The `RevmMiddleware` provides methods for sending the above instructions to an associated `Environment` so that you do not have to interact with the `Environment` directly! + +### Events +The `Environment` also emits Ethereum events and errors/reverts to clients who are set to listen to them. +To do so, we use a `tokio::sync::broadcast` channel and the `RevmMiddleware` manages subscriptions to these events. +As for errors or reverts, we are working on making the flow of handling these more graceful so that your own program or agents can decide how to handle them. + +## RevmMiddleware +The `RevmMiddleware` is the main interface for interacting with an `Environment`. +We implement the `ethers-rs` `Middleware` trait so that you may work with contract bindings generated by `forge` or `arbiter bind` as if you were interacting with a live network. +Not all methods are implemented, but the relevant ones are. + +`RevmMiddleware` owns a `Connection` which is the client's interface to the `Environment`'s `Socket`. +This `Connection` acts much like a WebSocket connection and is used to send `Instruction`s and receive their outcome from the `Environment` as well as subscribe to events. +To make this `Connection` and `RevmMiddleware` flexible, we also implement (for both) the `JsonRpcClient` and `PubSubClient` traits. + +We also provide `RevmMiddleware` a wallet so that it can be associated to an account in the `Environment`'s world state. +The `wallet: EOA` field of `RevmMiddleware` is decided upon creation of the `RevmMiddleware` and, if the wallet is generated from calling `RevmMiddleware::new()`, wallet will be of `EOA::Wallet(Wallet)` which allows for `RevmMiddleware` to sign transactions if need be. +It is possible to create accounts from a forked database, in which case you would call `RevmMiddleware::new_from_forked_eoa()` and the wallet would be of `EOA::Forked(Address)`. +This type is unable to sign as it is effectively impossible to recover the signing key from an address. +Fortunately, for almost every usecase of `RevmMiddleware`, you will not need to sign transactions, so this distinction does not matter. + +### Usage + +To create a `RevmMiddleware` that is associated with an account in the `Environment`'s world state, we can do the following: +```rust +use arbiter_core::middleware::RevmMiddleware; +use arbiter_core::environment::builder::EnvironmentBuilder; + +fn main() { + let env = EnvironmentBuilder::new().build(); + + // Create a client for the above `Environment` with an ID + let id = "alice"; + let alice = RevmMiddleware::new(&env, Some(id)); + + // Create a client without an ID + let client = RevmMiddleware::new(&env, None); +} +``` +These created clients can then get access to making calls and transactions to contracts deployed into the `Environment`'s world state. We can do the following: +```rust +use arbiter_core::middleware::RevmMiddleware; +use arbiter_core::environment::builder::EnvironmentBuilder; +use arbiter_bindings::bindings::arbiter_token::ArbiterToken; + +#[tokio::main] +async fn main() { + let env = EnvironmentBuilder::new().build(); + let client = RevmMiddleware::new(&env, None).unwrap(); + + // Deploy a contract + let contract = ArbiterToken::deploy(client, ("ARBT".to_owned(), "Arbiter Token".to_owned(), 18u8)).unwrap().send().await.unwrap(); +} +``` diff --git a/documentation/src/usage/arbiter_engine.md b/documentation/src/usage/arbiter_engine.md new file mode 100644 index 00000000..09db3bba --- /dev/null +++ b/documentation/src/usage/arbiter_engine.md @@ -0,0 +1 @@ +# Arbiter Engine diff --git a/documentation/src/usage/index.md b/documentation/src/usage/index.md new file mode 100644 index 00000000..8f04b05a --- /dev/null +++ b/documentation/src/usage/index.md @@ -0,0 +1 @@ +# Usage diff --git a/documentation/tests/skeptic.rs b/documentation/tests/skeptic.rs new file mode 100644 index 00000000..ff46c9c0 --- /dev/null +++ b/documentation/tests/skeptic.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/skeptic-tests.rs"));