├── .github ├── CODEOWNERS ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── dependencies.yml │ ├── integration.yml │ ├── lint.yml │ └── test.yml ├── .gitignore ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches ├── BENCHMARKS.md ├── Cargo.toml ├── README.md ├── artifacts │ ├── LargeFunction.json │ ├── Seaport.json │ ├── UniV4PoolManager.json │ └── UniswapV3Pool.json ├── benches │ ├── abi_encoding.rs │ ├── json_abi.rs │ ├── rlp.rs │ └── u256.rs ├── plots │ ├── dyn_encoding_bench.png │ └── static_encoding_bench.png └── src │ ├── alloy_helpers.rs │ ├── ethers_helpers.rs │ └── lib.rs ├── clippy.toml ├── examples ├── advanced │ ├── Cargo.toml │ └── examples │ │ ├── abi │ │ ├── BundleExecutor.sol │ │ ├── FlashBotsMultiCall.json │ │ └── SimpleLending.json │ │ ├── any_network.rs │ │ ├── decoding_json_abi.rs │ │ ├── encoding_dyn_abi.rs │ │ ├── encoding_sol_static.rs │ │ ├── foundry_fork_db.rs │ │ ├── reth_db_layer.rs │ │ ├── reth_db_provider.rs │ │ └── uniswap_u256 │ │ ├── alloy_profit.rs │ │ ├── alloy_simulation.rs │ │ ├── ethers_profit.rs │ │ ├── helpers │ │ ├── alloy.rs │ │ ├── ethers.rs │ │ └── mod.rs │ │ └── lib.rs ├── big-numbers │ ├── Cargo.toml │ └── examples │ │ ├── comparison_equivalence.rs │ │ ├── conversion.rs │ │ ├── create_instances.rs │ │ ├── math_operations.rs │ │ └── math_utilities.rs ├── comparison │ ├── Cargo.toml │ └── examples │ │ ├── compare_new_heads.rs │ │ └── compare_pending_txs.rs ├── contracts │ ├── Cargo.toml │ └── examples │ │ ├── abi │ │ ├── Colors.json │ │ └── IWETH9.json │ │ ├── arb_profit_calc.rs │ │ ├── artifacts │ │ ├── Counter.json │ │ ├── FlashBotsMultiCall.json │ │ └── FlashBotsMultiCall.sol │ │ ├── deploy_and_link_library.rs │ │ ├── deploy_from_artifact.rs │ │ ├── deploy_from_bytecode.rs │ │ ├── deploy_from_contract.rs │ │ ├── helpers.rs │ │ ├── interact_with_abi.rs │ │ ├── interact_with_contract_instance.rs │ │ ├── jsonrpc_error_decoding.rs │ │ ├── revert_decoding.rs │ │ ├── simulation_uni_v2.rs │ │ └── unknown_return_types.rs ├── fillers │ ├── Cargo.toml │ └── examples │ │ ├── gas_filler.rs │ │ ├── nonce_filler.rs │ │ ├── recommended_fillers.rs │ │ ├── urgent_filler.rs │ │ └── wallet_filler.rs ├── layers │ ├── Cargo.toml │ └── examples │ │ ├── delay_layer.rs │ │ ├── fallback_layer.rs │ │ ├── hyper_http_layer.rs │ │ ├── logging_layer.rs │ │ └── retry_layer.rs ├── node-bindings │ ├── Cargo.toml │ └── examples │ │ ├── anvil_deploy_contract.rs │ │ ├── anvil_fork_instance.rs │ │ ├── anvil_fork_provider.rs │ │ ├── anvil_local_instance.rs │ │ ├── anvil_local_provider.rs │ │ ├── anvil_set_storage_at.rs │ │ ├── geth_local_instance.rs │ │ └── reth_local_instance.rs ├── primitives │ ├── Cargo.toml │ └── examples │ │ ├── bytes_and_address_types.rs │ │ └── hashing_functions.rs ├── providers │ ├── Cargo.toml │ └── examples │ │ ├── abi │ │ └── IWETH9.json │ │ ├── basic_provider.rs │ │ ├── batch_rpc.rs │ │ ├── builder.rs │ │ ├── builtin.rs │ │ ├── dyn_provider.rs │ │ ├── embed_consensus_rpc.rs │ │ ├── http.rs │ │ ├── http_with_auth.rs │ │ ├── ipc.rs │ │ ├── mocking.rs │ │ ├── multicall.rs │ │ ├── multicall_batching.rs │ │ ├── wrapped_provider.rs │ │ ├── ws.rs │ │ └── ws_with_auth.rs ├── queries │ ├── Cargo.toml │ └── examples │ │ ├── query_contract_storage.rs │ │ ├── query_deployed_bytecode.rs │ │ └── query_logs.rs ├── sol-macro │ ├── Cargo.toml │ └── examples │ │ ├── abi │ │ └── Colors.json │ │ ├── all_derives.rs │ │ ├── decode_returns.rs │ │ ├── events_errors.rs │ │ ├── extra_derives.rs │ │ ├── structs_enums.rs │ │ └── user_defined_types.rs ├── subscriptions │ ├── Cargo.toml │ └── examples │ │ ├── abi │ │ └── IWETH9.json │ │ ├── event_multiplexer.rs │ │ ├── poll_logs.rs │ │ ├── subscribe_all_logs.rs │ │ ├── subscribe_blocks.rs │ │ ├── subscribe_logs.rs │ │ └── subscribe_pending_transactions.rs ├── transactions │ ├── Cargo.toml │ └── examples │ │ ├── artifacts │ │ ├── ERC20Example.json │ │ ├── Permit2.json │ │ └── SimpleStorage.json │ │ ├── debug_trace_call_many.rs │ │ ├── decode_input.rs │ │ ├── decode_receipt_log.rs │ │ ├── encode_decode_eip1559.rs │ │ ├── gas_price_usd.rs │ │ ├── permit2_signature_transfer.rs │ │ ├── send_eip1559_transaction.rs │ │ ├── send_eip4844_transaction.rs │ │ ├── send_eip7702_transaction.rs │ │ ├── send_legacy_transaction.rs │ │ ├── send_private_transaction.rs │ │ ├── send_raw_transaction.rs │ │ ├── trace_call.rs │ │ ├── trace_call_many.rs │ │ ├── trace_transaction.rs │ │ ├── transfer_erc20.rs │ │ ├── transfer_eth.rs │ │ └── with_access_list.rs └── wallets │ ├── Cargo.toml │ └── examples │ ├── aws_signer.rs │ ├── create_keystore.rs │ ├── ethereum_wallet.rs │ ├── gcp_signer.rs │ ├── keystore │ └── alice.json │ ├── keystore_signer.rs │ ├── ledger_signer.rs │ ├── mnemonic_signer.rs │ ├── private_key_signer.rs │ ├── sign_message.rs │ ├── sign_permit_hash.rs │ ├── trezor_signer.rs │ ├── verify_message.rs │ └── yubi_signer.rs ├── rustfmt.toml └── scripts └── test.sh /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @zerosnacks @yash-atreya @Evalir 2 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | ## Motivation 16 | 17 | 22 | 23 | ## Solution 24 | 25 | 29 | 30 | ## PR Checklist 31 | 32 | - [ ] Added Documentation 33 | - [ ] Breaking changes -------------------------------------------------------------------------------- /.github/workflows/dependencies.yml: -------------------------------------------------------------------------------- 1 | # Runs weeky `cargo update` to keep dependencies current. 2 | 3 | name: Dependencies 4 | 5 | on: 6 | schedule: 7 | # Run weekly 8 | - cron: "0 0 * * SUN" 9 | workflow_dispatch: 10 | # Needed so we can run it manually 11 | 12 | env: 13 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 14 | BRANCH: cargo-update 15 | TITLE: "chore(deps): weekly `cargo update`" 16 | BODY: | 17 | Automation to keep dependencies in `Cargo.lock` current. 18 | 19 |
cargo update log 20 |

21 | 22 | ```log 23 | $cargo_update_log 24 | ``` 25 | 26 |

27 |
28 | 29 | jobs: 30 | update: 31 | name: Update 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v4 35 | - uses: dtolnay/rust-toolchain@nightly 36 | 37 | - name: cargo update 38 | # Remove first line that always just says "Updating crates.io index" 39 | run: cargo update --color never 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log 40 | 41 | - name: craft commit message and PR body 42 | id: msg 43 | run: | 44 | export cargo_update_log="$(cat cargo_update.log)" 45 | 46 | echo "commit_message<> $GITHUB_OUTPUT 47 | printf "$TITLE\n\n$cargo_update_log\n" >> $GITHUB_OUTPUT 48 | echo "EOF" >> $GITHUB_OUTPUT 49 | 50 | echo "body<> $GITHUB_OUTPUT 51 | echo "$BODY" | envsubst >> $GITHUB_OUTPUT 52 | echo "EOF" >> $GITHUB_OUTPUT 53 | 54 | - name: Create Pull Request 55 | uses: peter-evans/create-pull-request@v6 56 | with: 57 | add-paths: ./Cargo.lock 58 | commit-message: ${{ steps.msg.outputs.commit_message }} 59 | title: ${{ env.TITLE }} 60 | body: ${{ steps.msg.outputs.body }} 61 | branch: ${{ env.BRANCH }} 62 | -------------------------------------------------------------------------------- /.github/workflows/integration.yml: -------------------------------------------------------------------------------- 1 | # Runs Alloy integration tests daily to ensure compatibility with the latest version of Alloy. 2 | 3 | name: Integration 4 | 5 | on: 6 | schedule: 7 | # Run daily 8 | - cron: "0 0 * * *" 9 | workflow_dispatch: 10 | # Needed so we can run it manually 11 | 12 | jobs: 13 | integration: 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 30 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: dtolnay/rust-toolchain@stable 19 | - uses: foundry-rs/foundry-toolchain@v1 20 | - uses: Swatinem/rust-cache@v2 21 | with: 22 | cache-on-failure: true 23 | - name: Run examples 24 | run: | 25 | # Run examples with the current version of Alloy 26 | ./scripts/test.sh 27 | 28 | # Fetch the latest commit hash of the `main` branch from the Alloy repository 29 | export latest_alloy_commit=$(git ls-remote https://github.com/alloy-rs/alloy.git \ 30 | | grep refs/heads/main \ 31 | | cut -f 1) 32 | 33 | # Use the commit hash to update the rev in Cargo.toml 34 | sed -i '/alloy = { version = "[^"]*", features = \[/,/\] }/s/alloy = { version = "[^"]*",/alloy = { git = "https:\/\/github.com\/alloy-rs\/alloy", rev = "'"$latest_alloy_commit"'",/' \ 35 | Cargo.toml 36 | 37 | # Update to the latest commit 38 | cargo update 39 | 40 | # Run the examples with the latest version of Alloy 41 | ./scripts/test.sh 42 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | # Runs `clippy` and `cargo fmt` checks. 2 | 3 | name: Lint 4 | 5 | on: 6 | push: 7 | branches: [main] 8 | pull_request: 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | clippy: 19 | runs-on: ubuntu-latest 20 | timeout-minutes: 5 21 | steps: 22 | - uses: actions/checkout@v3 23 | - uses: dtolnay/rust-toolchain@clippy 24 | - uses: Swatinem/rust-cache@v2 25 | with: 26 | cache-on-failure: true 27 | - run: cargo +nightly clippy --examples --all-features 28 | env: 29 | RUSTFLAGS: -Dwarnings 30 | 31 | fmt: 32 | runs-on: ubuntu-latest 33 | timeout-minutes: 5 34 | steps: 35 | - uses: actions/checkout@v3 36 | - uses: dtolnay/rust-toolchain@nightly 37 | with: 38 | components: rustfmt 39 | - run: cargo +nightly fmt --all --check 40 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # Runs build checks and examples. 2 | 3 | name: Test 4 | 5 | on: 6 | push: 7 | branches: [main] 8 | pull_request: 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | checks: 19 | runs-on: ubuntu-latest 20 | timeout-minutes: 5 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: dtolnay/rust-toolchain@stable 24 | - uses: taiki-e/install-action@cargo-hack 25 | - uses: Swatinem/rust-cache@v2 26 | with: 27 | cache-on-failure: true 28 | - name: cargo hack 29 | run: cargo hack check --feature-powerset --depth 2 30 | 31 | examples: 32 | runs-on: ubuntu-latest 33 | timeout-minutes: 15 34 | steps: 35 | - uses: actions/checkout@v4 36 | - uses: dtolnay/rust-toolchain@stable 37 | - uses: foundry-rs/foundry-toolchain@v1 38 | - uses: Swatinem/rust-cache@v2 39 | with: 40 | cache-on-failure: true 41 | - name: Run examples 42 | run: ./scripts/test.sh 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | .vscode 4 | .idea 5 | .env 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /benches/BENCHMARKS.md: -------------------------------------------------------------------------------- 1 | # Benchmarks 2 | 3 | ## Table of Contents 4 | 5 | - [Benchmark Results](#benchmark-results) 6 | - [ABI Encoding](#abi-encoding) 7 | - [JSON-ABI Serialization](#json-abi-serialization) 8 | - [JSON-ABI Deserialization](#json-abi-deserialization) 9 | - [Serde Function Signature](#serde-function-signature) 10 | - [Rlp Encoding and Decoding](#rlp-encoding-and-decoding) 11 | - [U256 Operations](#u256-operations) 12 | 13 | ## Benchmark Results 14 | 15 | ### ABI Encoding 16 | 17 | | | `Ethers` | `Alloy` | 18 | | :------------ | :----------------------- | :-------------------------------- | 19 | | **`Static`** | `1.12 us` (✅ **1.00x**) | `90.89 ns` (🚀 **12.32x faster**) | 20 | | **`Dynamic`** | `2.20 us` (✅ **1.00x**) | `1.88 us` (✅ **1.17x faster**) | 21 | 22 | ### JSON-ABI Serialization 23 | 24 | | | `EthAbi` | `Alloy` | 25 | | :------------------ | :------------------------ | :------------------------------- | 26 | | **`Seaport`** | `35.43 us` (✅ **1.00x**) | `38.68 us` (✅ **1.09x slower**) | 27 | | **`PoolManager`** | `18.33 us` (✅ **1.00x**) | `17.94 us` (✅ **1.02x faster**) | 28 | | **`UniswapV3Pool`** | `14.61 us` (✅ **1.00x**) | `12.99 us` (✅ **1.12x faster**) | 29 | 30 | ### JSON-ABI Deserialization 31 | 32 | | | `EthAbi` | `Alloy` | 33 | | :------------------ | :------------------------- | :-------------------------------- | 34 | | **`Seaport`** | `209.43 us` (✅ **1.00x**) | `210.67 us` (✅ **1.01x slower**) | 35 | | **`PoolManager`** | `89.05 us` (✅ **1.00x**) | `93.31 us` (✅ **1.05x slower**) | 36 | | **`UniswapV3Pool`** | `63.24 us` (✅ **1.00x**) | `68.50 us` (✅ **1.08x slower**) | 37 | 38 | ### Serde Function Signature 39 | 40 | | | `EthAbi` | `Alloy` | 41 | | :---------------- | :------------------------ | :--------------------------------- | 42 | | **`Serialize`** | `5.03 us` (✅ **1.00x**) | `247.82 ns` (🚀 **20.29x faster**) | 43 | | **`Deserialize`** | `14.10 us` (✅ **1.00x**) | `14.05 us` (✅ **1.00x faster**) | 44 | 45 | ### Rlp Encoding and Decoding 46 | 47 | | | `Parity-Rlp` | `Alloy-Rlp` | 48 | | :------------- | :------------------------ | :------------------------------- | 49 | | **`Encoding`** | `86.70 ns` (✅ **1.00x**) | `26.88 ns` (🚀 **3.23x faster**) | 50 | | **`Decoding`** | `88.79 ns` (✅ **1.00x**) | `21.43 ns` (🚀 **4.14x faster**) | 51 | 52 | ### U256 Operations 53 | 54 | | | `Ethers` | `Alloy` | 55 | | :-------------- | :------------------------- | :-------------------------------- | 56 | | **`amountIn`** | `512.47 ns` (✅ **1.00x**) | `216.32 ns` (🚀 **2.37x faster**) | 57 | | **`amountOut`** | `53.82 ns` (✅ **1.00x**) | `18.19 ns` (🚀 **2.96x faster**) | 58 | 59 | --- 60 | 61 | Made with [criterion-table](https://github.com/nu11ptr/criterion-table) 62 | -------------------------------------------------------------------------------- /benches/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "alloy_benches" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | alloy.workspace = true 9 | criterion.workspace = true 10 | ethers.workspace = true 11 | eyre.workspace = true 12 | serde_json.workspace = true 13 | tokio.workspace = true 14 | ethabi = "18" 15 | rlp = "0.6.1" 16 | rlp-derive = "0.2.0" 17 | alloy-rlp = "0.3.11" 18 | 19 | [lib] 20 | name = "alloy_benches" 21 | path = "src/lib.rs" 22 | 23 | [[bench]] 24 | name = "abi_encoding" 25 | harness = false 26 | 27 | [[bench]] 28 | name = "u256" 29 | harness = false 30 | 31 | [[bench]] 32 | name = "json_abi" 33 | harness = false 34 | 35 | [[bench]] 36 | name = "rlp" 37 | harness = false 38 | -------------------------------------------------------------------------------- /benches/artifacts/LargeFunction.json: -------------------------------------------------------------------------------- 1 | {"type":"function","name":"fulfillAvailableAdvancedOrders","inputs":[{"name":"","type":"tuple[]","internalType":"struct AdvancedOrder[]","components":[{"name":"parameters","type":"tuple","internalType":"struct OrderParameters","components":[{"name":"offerer","type":"address","internalType":"address"},{"name":"zone","type":"address","internalType":"address"},{"name":"offer","type":"tuple[]","internalType":"struct OfferItem[]","components":[{"name":"itemType","type":"uint8","internalType":"enum ItemType"},{"name":"token","type":"address","internalType":"address"},{"name":"identifierOrCriteria","type":"uint256","internalType":"uint256"},{"name":"startAmount","type":"uint256","internalType":"uint256"},{"name":"endAmount","type":"uint256","internalType":"uint256"}]},{"name":"consideration","type":"tuple[]","internalType":"struct ConsiderationItem[]","components":[{"name":"itemType","type":"uint8","internalType":"enum ItemType"},{"name":"token","type":"address","internalType":"address"},{"name":"identifierOrCriteria","type":"uint256","internalType":"uint256"},{"name":"startAmount","type":"uint256","internalType":"uint256"},{"name":"endAmount","type":"uint256","internalType":"uint256"},{"name":"recipient","type":"address","internalType":"address payable"}]},{"name":"orderType","type":"uint8","internalType":"enum OrderType"},{"name":"startTime","type":"uint256","internalType":"uint256"},{"name":"endTime","type":"uint256","internalType":"uint256"},{"name":"zoneHash","type":"bytes32","internalType":"bytes32"},{"name":"salt","type":"uint256","internalType":"uint256"},{"name":"conduitKey","type":"bytes32","internalType":"bytes32"},{"name":"totalOriginalConsiderationItems","type":"uint256","internalType":"uint256"}]},{"name":"numerator","type":"uint120","internalType":"uint120"},{"name":"denominator","type":"uint120","internalType":"uint120"},{"name":"signature","type":"bytes","internalType":"bytes"},{"name":"extraData","type":"bytes","internalType":"bytes"}]},{"name":"","type":"tuple[]","internalType":"struct CriteriaResolver[]","components":[{"name":"orderIndex","type":"uint256","internalType":"uint256"},{"name":"side","type":"uint8","internalType":"enum Side"},{"name":"index","type":"uint256","internalType":"uint256"},{"name":"identifier","type":"uint256","internalType":"uint256"},{"name":"criteriaProof","type":"bytes32[]","internalType":"bytes32[]"}]},{"name":"","type":"tuple[][]","internalType":"struct FulfillmentComponent[][]","components":[{"name":"orderIndex","type":"uint256","internalType":"uint256"},{"name":"itemIndex","type":"uint256","internalType":"uint256"}]},{"name":"","type":"tuple[][]","internalType":"struct FulfillmentComponent[][]","components":[{"name":"orderIndex","type":"uint256","internalType":"uint256"},{"name":"itemIndex","type":"uint256","internalType":"uint256"}]},{"name":"fulfillerConduitKey","type":"bytes32","internalType":"bytes32"},{"name":"recipient","type":"address","internalType":"address"},{"name":"maximumFulfilled","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool[]","internalType":"bool[]"},{"name":"","type":"tuple[]","internalType":"struct Execution[]","components":[{"name":"item","type":"tuple","internalType":"struct ReceivedItem","components":[{"name":"itemType","type":"uint8","internalType":"enum ItemType"},{"name":"token","type":"address","internalType":"address"},{"name":"identifier","type":"uint256","internalType":"uint256"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"recipient","type":"address","internalType":"address payable"}]},{"name":"offerer","type":"address","internalType":"address"},{"name":"conduitKey","type":"bytes32","internalType":"bytes32"}]}],"stateMutability":"payable"} -------------------------------------------------------------------------------- /benches/benches/json_abi.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarks alloy JSON-ABI serialization and deserialization against ethabi. 2 | #![allow(unknown_lints, clippy::incompatible_msrv)] 3 | 4 | use alloy::json_abi::{Function, JsonAbi}; 5 | use criterion::{ 6 | criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, 7 | }; 8 | use std::{hint::black_box, time::Duration}; 9 | 10 | fn ser_group(c: &mut Criterion) { 11 | let mut g = c.benchmark_group("JSON-ABI Serialization"); 12 | g.warm_up_time(Duration::from_secs(3)); 13 | ser(&mut g, "Seaport", include_str!("../artifacts/Seaport.json")); 14 | ser(&mut g, "PoolManager", include_str!("../artifacts/UniV4PoolManager.json")); 15 | ser(&mut g, "UniswapV3Pool", include_str!("../artifacts/UniswapV3Pool.json")); 16 | } 17 | 18 | fn deser_group(c: &mut Criterion) { 19 | let mut g = c.benchmark_group("JSON-ABI Deserialization"); 20 | g.warm_up_time(Duration::from_secs(3)); 21 | deser(&mut g, "Seaport", include_str!("../artifacts/Seaport.json")); 22 | deser(&mut g, "PoolManager", include_str!("../artifacts/UniV4PoolManager.json")); 23 | deser(&mut g, "UniswapV3Pool", include_str!("../artifacts/UniswapV3Pool.json")); 24 | } 25 | 26 | fn deser(g: &mut BenchmarkGroup<'_, WallTime>, name: &str, s: &str) { 27 | type A = JsonAbi; 28 | type E = ethabi::Contract; 29 | 30 | g.bench_function(format!("EthAbi/{name}"), |b| { 31 | b.iter(|| -> E { serde_json::from_str(black_box(s)).unwrap() }); 32 | }); 33 | 34 | g.bench_function(format!("Alloy/{name}"), |b| { 35 | b.iter(|| -> A { serde_json::from_str(black_box(s)).unwrap() }); 36 | }); 37 | } 38 | 39 | fn ser(g: &mut BenchmarkGroup<'_, WallTime>, name: &str, s: &str) { 40 | type A = JsonAbi; 41 | type E = ethabi::Contract; 42 | 43 | g.bench_function(format!("EthAbi/{name}"), |b| { 44 | let abi = serde_json::from_str::(s).unwrap(); 45 | b.iter(|| serde_json::to_string(black_box(&abi)).unwrap()); 46 | }); 47 | 48 | g.bench_function(format!("Alloy/{name}"), |b| { 49 | let abi = serde_json::from_str::(s).unwrap(); 50 | b.iter(|| serde_json::to_string(black_box(&abi)).unwrap()); 51 | }); 52 | } 53 | 54 | fn signature(c: &mut Criterion) { 55 | let mut g = c.benchmark_group("Serde Function Signature"); 56 | g.warm_up_time(Duration::from_secs(1)); 57 | serde_signature(&mut g, include_str!("../artifacts/LargeFunction.json")); 58 | } 59 | 60 | fn serde_signature(g: &mut BenchmarkGroup<'_, WallTime>, s: &str) { 61 | let mut alloy = serde_json::from_str::(s).unwrap(); 62 | let mut ethabi = serde_json::from_str::(s).unwrap(); 63 | 64 | assert_eq!(alloy.selector(), ethabi.short_signature()); 65 | 66 | // clear outputs so ethabi doesn't format them 67 | alloy.outputs.clear(); 68 | ethabi.outputs.clear(); 69 | 70 | assert_eq!(alloy.selector(), ethabi.short_signature()); 71 | assert_eq!(alloy.signature(), ethabi.signature()); 72 | 73 | g.bench_function("EthAbi/Serialize", |b| { 74 | b.iter(|| black_box(ðabi).signature()); 75 | }); 76 | 77 | g.bench_function("Alloy/Serialize", |b| { 78 | b.iter(|| black_box(&alloy).signature()); 79 | }); 80 | 81 | g.bench_with_input("EthAbi/Deserialize", s, |b, s| { 82 | b.iter(|| { 83 | let _f: ethabi::Function = serde_json::from_str(black_box(s)).unwrap(); 84 | }); 85 | }); 86 | 87 | g.bench_with_input("Alloy/Deserialize", s, |b, s| { 88 | b.iter(|| { 89 | let _f: Function = serde_json::from_str(black_box(s)).unwrap(); 90 | }); 91 | }); 92 | } 93 | 94 | criterion_group!(benches, ser_group, deser_group, signature); 95 | criterion_main!(benches); 96 | -------------------------------------------------------------------------------- /benches/benches/rlp.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarking alloy RLP encode and decode against parity-rlp. 2 | 3 | use alloy_rlp::{Decodable, Encodable, RlpDecodable, RlpEncodable}; 4 | 5 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 6 | use rlp::Encodable as ParityEncodable; 7 | 8 | #[derive(RlpEncodable, RlpDecodable, rlp_derive::RlpDecodable, rlp_derive::RlpEncodable)] 9 | pub struct MyStruct { 10 | pub a: u128, 11 | pub b: Vec, 12 | } 13 | 14 | fn rlp(c: &mut Criterion) { 15 | let mut g = c.benchmark_group("Rlp Encoding and Decoding"); 16 | g.warm_up_time(std::time::Duration::from_secs(3)); 17 | 18 | let my_struct = MyStruct { a: 42, b: vec![1, 2, 3, 4, 5] }; 19 | 20 | // Parity RLP encoding 21 | g.bench_with_input("Parity-Rlp/Encoding", &my_struct, |b, my_struct| { 22 | b.iter(|| { 23 | let out = my_struct.rlp_bytes(); 24 | black_box(out); 25 | }) 26 | }); 27 | // Alloy RLP encoding 28 | g.bench_with_input("Alloy-Rlp/Encoding", &my_struct, |b, my_struct| { 29 | b.iter(|| { 30 | let mut out = Vec::new(); 31 | my_struct.encode(&mut out); 32 | black_box(out); 33 | }) 34 | }); 35 | 36 | let mut encoded = Vec::new(); 37 | my_struct.encode(&mut encoded); 38 | 39 | // Parity RLP decoding 40 | g.bench_with_input("Parity-Rlp/Decoding", &encoded, |b, encoded| { 41 | b.iter(|| { 42 | let decoded: MyStruct = rlp::decode(encoded).unwrap(); 43 | black_box(decoded); 44 | }) 45 | }); 46 | 47 | // Alloy RLP decoding 48 | g.bench_with_input("Alloy-Rlp/Decoding", &encoded, |b, encoded| { 49 | b.iter(|| { 50 | let decoded = MyStruct::decode(&mut encoded.as_slice()).unwrap(); 51 | black_box(decoded); 52 | }) 53 | }); 54 | } 55 | 56 | criterion_group!(benches, rlp); 57 | criterion_main!(benches); 58 | -------------------------------------------------------------------------------- /benches/benches/u256.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::U256 as aU256; 2 | 3 | use criterion::{criterion_group, criterion_main, Criterion}; 4 | use ethers::types::U256; 5 | use std::{hint::black_box, str::FromStr}; 6 | 7 | use alloy_benches::{ 8 | alloy_helpers::{ 9 | get_amount_in as a_get_amount_in, get_amount_out as a_get_amount_out, 10 | get_sushi_pair as a_get_sushi_pair, get_uniswap_pair as a_get_uniswap_pair, 11 | }, 12 | ethers_helpers::{ 13 | get_amount_in as e_get_amount_in, get_amount_out as e_get_amount_out, 14 | get_sushi_pair as e_get_sushi_pair, get_uniswap_pair as e_get_uniswap_pair, 15 | }, 16 | }; 17 | 18 | fn u256_benchmark(c: &mut Criterion) { 19 | let a_uniswap_pair = a_get_uniswap_pair(); 20 | let a_sushi_pair = a_get_sushi_pair(); 21 | let e_uniswap_pair = e_get_uniswap_pair(); 22 | let e_sushi_pair = e_get_sushi_pair(); 23 | 24 | let a_amount_in = aU256::from_str("1000000000000000000").unwrap(); 25 | let e_amount_in = U256::from_dec_str("1000000000000000000").unwrap(); 26 | 27 | let mut group1 = c.benchmark_group("U256 Operations"); 28 | 29 | group1.bench_function("Ethers/getAmountIn", |b| { 30 | b.iter(|| { 31 | _ = e_get_amount_in( 32 | black_box(e_uniswap_pair.reserve0), 33 | black_box(e_uniswap_pair.reserve1), 34 | black_box(false), 35 | black_box(e_sushi_pair.reserve0), 36 | black_box(e_sushi_pair.reserve1), 37 | ); 38 | }) 39 | }); 40 | 41 | group1.bench_function("Alloy/getAmountIn", |b| { 42 | b.iter(|| { 43 | _ = a_get_amount_in( 44 | black_box(a_uniswap_pair.reserve0), 45 | black_box(a_uniswap_pair.reserve1), 46 | black_box(false), 47 | black_box(a_sushi_pair.reserve0), 48 | black_box(a_sushi_pair.reserve1), 49 | ); 50 | }) 51 | }); 52 | 53 | group1.bench_function("Ethers/getAmountOut", |b| { 54 | b.iter(|| { 55 | _ = e_get_amount_out( 56 | black_box(e_uniswap_pair.reserve0), 57 | black_box(e_uniswap_pair.reserve1), 58 | black_box(e_amount_in), 59 | ); 60 | }) 61 | }); 62 | 63 | group1.bench_function("Alloy/getAmountOut", |b| { 64 | b.iter(|| { 65 | _ = a_get_amount_out( 66 | black_box(a_uniswap_pair.reserve0), 67 | black_box(a_uniswap_pair.reserve1), 68 | black_box(a_amount_in), 69 | ); 70 | }) 71 | }); 72 | 73 | group1.finish(); 74 | } 75 | 76 | criterion_group!(benches, u256_benchmark); 77 | criterion_main!(benches); 78 | -------------------------------------------------------------------------------- /benches/plots/dyn_encoding_bench.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alloy-rs/examples/5ab85a6c9f68b64dd93168e0540048cd80d964fd/benches/plots/dyn_encoding_bench.png -------------------------------------------------------------------------------- /benches/plots/static_encoding_bench.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alloy-rs/examples/5ab85a6c9f68b64dd93168e0540048cd80d964fd/benches/plots/static_encoding_bench.png -------------------------------------------------------------------------------- /benches/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod alloy_helpers; 2 | pub mod ethers_helpers; 3 | -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | msrv = "1.83" 2 | -------------------------------------------------------------------------------- /examples/advanced/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples-advanced" 3 | publish.workspace = true 4 | version.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dependencies] 16 | foundry-fork-db = "0.14" 17 | alloy.workspace = true 18 | ethers.workspace = true 19 | alloy-evm = "0.7.2" 20 | 21 | # reth 22 | revm = { version = "23.1.0", default-features = false } 23 | revm-primitives = { version = "19.0.0", default-features = false } 24 | reth-db = { git = "https://github.com/paradigmxyz/reth", package = "reth-db", rev = "55f4b0b" } 25 | reth-provider = { git = "https://github.com/paradigmxyz/reth", package = "reth-provider", rev = "55f4b0b" } 26 | reth-node-types = { git = "https://github.com/paradigmxyz/reth", package = "reth-node-types", rev = "55f4b0b" } 27 | reth-chainspec = { git = "https://github.com/paradigmxyz/reth", package = "reth-chainspec", rev = "55f4b0b" } 28 | reth-node-ethereum = { git = "https://github.com/paradigmxyz/reth", package = "reth-node-ethereum", rev = "55f4b0b" } 29 | 30 | eyre.workspace = true 31 | tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } 32 | serde = { workspace = true, features = ["derive"] } 33 | serde_json = { workspace = true } 34 | 35 | 36 | [[bin]] 37 | name = "alloy_profit" 38 | path = "examples/uniswap_u256/alloy_profit.rs" 39 | 40 | [[bin]] 41 | name = "alloy_simulation" 42 | path = "examples/uniswap_u256/alloy_simulation.rs" 43 | 44 | [[bin]] 45 | name = "ethers_profit" 46 | path = "examples/uniswap_u256/ethers_profit.rs" 47 | 48 | [lib] 49 | name = "uniswap_u256" 50 | path = "examples/uniswap_u256/lib.rs" 51 | -------------------------------------------------------------------------------- /examples/advanced/examples/abi/BundleExecutor.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | interface IERC20 { 7 | event Approval(address indexed owner, address indexed spender, uint value); 8 | event Transfer(address indexed from, address indexed to, uint value); 9 | 10 | function name() external view returns (string memory); 11 | function symbol() external view returns (string memory); 12 | function decimals() external view returns (uint8); 13 | function totalSupply() external view returns (uint); 14 | function balanceOf(address owner) external view returns (uint); 15 | function allowance( 16 | address owner, 17 | address spender 18 | ) external view returns (uint); 19 | 20 | function approve(address spender, uint value) external returns (bool); 21 | function transfer(address to, uint value) external returns (bool); 22 | function transferFrom( 23 | address from, 24 | address to, 25 | uint value 26 | ) external returns (bool); 27 | } 28 | 29 | interface IWETH is IERC20 { 30 | function deposit() external payable; 31 | function withdraw(uint) external; 32 | } 33 | 34 | // This contract simply calls multiple targets sequentially, ensuring WETH balance before and after 35 | 36 | contract FlashBotsMultiCall { 37 | address private immutable owner; 38 | address private immutable executor; 39 | IWETH private constant WETH = 40 | IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 41 | 42 | modifier onlyExecutor() { 43 | require(msg.sender == executor); 44 | _; 45 | } 46 | 47 | modifier onlyOwner() { 48 | require(msg.sender == owner); 49 | _; 50 | } 51 | 52 | constructor(address _executor) payable { 53 | owner = msg.sender; 54 | executor = _executor; 55 | if (msg.value > 0) { 56 | WETH.deposit{value: msg.value}(); 57 | } 58 | } 59 | 60 | receive() external payable {} 61 | 62 | function uniswapWeth( 63 | uint256 _wethAmountToFirstMarket, 64 | uint256 _ethAmountToCoinbase, 65 | address[] memory _targets, 66 | bytes[] memory _payloads 67 | ) external payable onlyExecutor { 68 | require(_targets.length == _payloads.length); 69 | uint256 _wethBalanceBefore = WETH.balanceOf(address(this)); 70 | WETH.transfer(_targets[0], _wethAmountToFirstMarket); 71 | for (uint256 i = 0; i < _targets.length; i++) { 72 | (bool _success, bytes memory _response) = _targets[i].call( 73 | _payloads[i] 74 | ); 75 | require(_success); 76 | _response; 77 | } 78 | 79 | uint256 _wethBalanceAfter = WETH.balanceOf(address(this)); 80 | require(_wethBalanceAfter > _wethBalanceBefore + _ethAmountToCoinbase); 81 | if (_ethAmountToCoinbase == 0) return; 82 | 83 | uint256 _ethBalance = address(this).balance; 84 | if (_ethBalance < _ethAmountToCoinbase) { 85 | WETH.withdraw(_ethAmountToCoinbase - _ethBalance); 86 | } 87 | block.coinbase.transfer(_ethAmountToCoinbase); 88 | } 89 | 90 | function call( 91 | address payable _to, 92 | uint256 _value, 93 | bytes calldata _data 94 | ) external payable onlyOwner returns (bytes memory) { 95 | require(_to != address(0)); 96 | (bool _success, bytes memory _result) = _to.call{value: _value}(_data); 97 | require(_success); 98 | return _result; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /examples/advanced/examples/any_network.rs: -------------------------------------------------------------------------------- 1 | //! Example of using `AnyNetwork` to get a type-safe representation of 2 | //! network-specific data. 3 | //! 4 | //! In this example, we extract the `gasUsedForL1` and `l1BlockNumber` fields 5 | //! of Arbitrum's transaction receipts. 6 | 7 | use alloy::{ 8 | network::AnyNetwork, 9 | primitives::{address, Address, U128, U256, U64}, 10 | providers::ProviderBuilder, 11 | signers::local::PrivateKeySigner, 12 | sol, 13 | }; 14 | use eyre::Result; 15 | 16 | // The address of the contract below deployed to Arbitrum Sepolia. 17 | const COUNTER_CONTRACT_ADDRESS: Address = address!("d62FC4aB418580919F22E2aC3A0D93F832A95E70"); 18 | 19 | sol! { 20 | #[allow(missing_docs)] 21 | #[sol(rpc)] 22 | contract Counter { 23 | uint256 public number; 24 | 25 | function setNumber(uint256 newNumber) public { 26 | number = newNumber; 27 | } 28 | 29 | function increment() public { 30 | number++; 31 | } 32 | } 33 | } 34 | 35 | #[derive(Debug, serde::Deserialize)] 36 | struct ArbOtherFields { 37 | #[serde(rename = "gasUsedForL1")] 38 | gas_used_for_l1: U128, 39 | #[serde(rename = "l1BlockNumber")] 40 | l1_block_number: U64, 41 | } 42 | 43 | #[tokio::main] 44 | async fn main() -> Result<()> { 45 | // [RISK WARNING! Writing a private key in the code file is insecure behavior.] 46 | // The following code is for testing only. Set up signer from private key, be aware of danger. 47 | let signer: PrivateKeySigner = "".parse().expect("should parse private key"); 48 | 49 | // Create a provider with the Arbitrum Sepolia network and the wallet. 50 | let rpc_url = "https://sepolia-rollup.arbitrum.io/rpc".parse()?; 51 | let provider = 52 | ProviderBuilder::new().network::().wallet(signer).connect_http(rpc_url); 53 | 54 | // Create a contract instance. 55 | let contract = Counter::new(COUNTER_CONTRACT_ADDRESS, &provider); 56 | 57 | // Set the number to 42. 58 | let builder = contract.setNumber(U256::from(42)); 59 | let receipt = builder.send().await?.get_receipt().await?; 60 | 61 | // Fetch the `gasUsedForL1` and `l1BlockNumber` fields from the receipt. 62 | let arb_fields: ArbOtherFields = receipt.other.deserialize_into()?; 63 | let l1_gas = arb_fields.gas_used_for_l1.to::(); 64 | let l1_block_number = arb_fields.l1_block_number.to::(); 65 | 66 | println!("Gas used for L1: {l1_gas}"); 67 | println!("L1 block number: {l1_block_number}"); 68 | 69 | Ok(()) 70 | } 71 | -------------------------------------------------------------------------------- /examples/advanced/examples/decoding_json_abi.rs: -------------------------------------------------------------------------------- 1 | //! Example for deserializing ABI using `json_abi`. 2 | 3 | use alloy::json_abi::JsonAbi; 4 | 5 | fn main() -> Result<(), Box> { 6 | // Get the contract abi. 7 | let path = std::env::current_dir()?.join("examples/advanced/examples/abi/SimpleLending.json"); 8 | let contents = std::fs::read(path)?; 9 | let abi: JsonAbi = serde_json::from_slice(&contents)?; 10 | 11 | // Print deserialized ABI components 12 | println!("Deserialized ABI:"); 13 | 14 | // Constructor 15 | if let Some(constructor) = &abi.constructor { 16 | println!("\n>> Constructor:"); 17 | println!(" Inputs: {:?}", constructor.inputs); 18 | println!(" State mutability: {:?}", constructor.state_mutability); 19 | } 20 | 21 | println!("\n=========\n"); 22 | 23 | // Functions 24 | println!("Functions:"); 25 | for (name, functions) in &abi.functions { 26 | println!("\n>> {name}:"); 27 | for function in functions { 28 | println!(" Inputs: {:?}", function.inputs); 29 | println!(" Outputs: {:?}", function.outputs); 30 | println!(" State mutability: {:?}", function.state_mutability); 31 | } 32 | } 33 | 34 | println!("\n=========\n"); 35 | 36 | // Events 37 | println!("Events:"); 38 | for (name, events) in &abi.events { 39 | println!("\n>> {name}:"); 40 | for event in events { 41 | println!(" Inputs: {:?}", event.inputs); 42 | println!(" Anonymous: {}", event.anonymous); 43 | } 44 | } 45 | 46 | println!("\n=========\n"); 47 | 48 | // Errors 49 | println!("Errors:"); 50 | for (name, errors) in &abi.errors { 51 | println!(">> {name}:"); 52 | for error in errors { 53 | println!(" Inputs: {:?}", error.inputs); 54 | } 55 | } 56 | 57 | println!("\n=========\n"); 58 | 59 | // Example of working with a specific function 60 | if let Some(add_collateral) = abi.functions.get("addCollateral").and_then(|f| f.first()) { 61 | println!("Example: addCollateral() function exists!"); 62 | println!("Inputs:"); 63 | for input in &add_collateral.inputs { 64 | println!(" Name: {}, Type: {}", input.name, input.ty); 65 | } 66 | } 67 | 68 | Ok(()) 69 | } 70 | -------------------------------------------------------------------------------- /examples/advanced/examples/encoding_dyn_abi.rs: -------------------------------------------------------------------------------- 1 | //! Example of [EIP712](https://eips.ethereum.org/EIPS/eip-712) encoding and decoding via `dyn_abi`. 2 | 3 | use alloy::{ 4 | dyn_abi::{DynSolType, DynSolValue}, 5 | hex, 6 | primitives::{keccak256, Address, U256}, 7 | signers::{local::PrivateKeySigner, Signer}, 8 | }; 9 | 10 | #[tokio::main] 11 | async fn main() -> Result<(), Box> { 12 | // EIP-712 domain 13 | let domain_type = DynSolType::Tuple(vec![ 14 | DynSolType::String, // name 15 | DynSolType::String, // version 16 | DynSolType::Uint(256), // chainId 17 | DynSolType::Address, // verifyingContract 18 | ]); 19 | 20 | let domain_value = DynSolValue::Tuple(vec![ 21 | DynSolValue::String("Alloy".to_string()), 22 | DynSolValue::String("1.0.1".to_string()), 23 | DynSolValue::Uint(U256::from(1), 256), 24 | DynSolValue::Address(Address::from([0x42; 20])), 25 | ]); 26 | 27 | // Message type (sample message) 28 | let message_type = DynSolType::Tuple(vec![ 29 | DynSolType::Address, // from 30 | DynSolType::Address, // to 31 | DynSolType::String, // contents 32 | ]); 33 | 34 | // Random values 35 | let message_value = DynSolValue::Tuple(vec![ 36 | DynSolValue::Address(Address::from([0x11; 20])), 37 | DynSolValue::Address(Address::from([0x22; 20])), 38 | DynSolValue::String("EIP-712 encoding".to_string()), 39 | ]); 40 | 41 | // Encode the domain and message 42 | let encoded_domain = domain_value.abi_encode(); 43 | let encoded_message = message_value.abi_encode(); 44 | 45 | println!("Encoded domain: 0x{}", hex::encode(&encoded_domain)); 46 | println!("Encoded message: 0x{}", hex::encode(&encoded_message)); 47 | 48 | // Decode the domain and message 49 | let decoded_domain = domain_type.abi_decode(&encoded_domain)?; 50 | let decoded_message = message_type.abi_decode(&encoded_message)?; 51 | 52 | println!("\nDecoded domain:"); 53 | print_tuple(&decoded_domain, &["name", "version", "chainId", "verifyingContract"]); 54 | 55 | println!("\nDecoded message:"); 56 | print_tuple(&decoded_message, &["from", "to", "contents"]); 57 | 58 | // Calculate EIP-712 hash 59 | let domain_separator = keccak256(&encoded_domain); 60 | let message_hash = keccak256(&encoded_message); 61 | let eip712_hash = keccak256([&[0x19, 0x01], &domain_separator[..], &message_hash[..]].concat()); 62 | 63 | println!("\nEIP-712 hash: 0x{}", hex::encode(eip712_hash)); 64 | 65 | // Signing the hash via random signer 66 | // Ref: examples/wallets/examples/sign_message.rs 67 | 68 | // Create a signer 69 | let wallet = PrivateKeySigner::random(); 70 | println!("\nSigner address: {}", wallet.address()); 71 | 72 | // Sign the EIP-712 hash 73 | let signature = wallet.sign_hash(&eip712_hash).await?; 74 | println!("Signature: 0x{}", hex::encode(signature.as_bytes())); 75 | 76 | // Verify the signature 77 | let recovered_address = signature.recover_address_from_prehash(&eip712_hash)?; 78 | println!("Recovered address: {recovered_address}"); 79 | 80 | assert_eq!(recovered_address, wallet.address(), "Signature verification failed"); 81 | println!("Signature verified successfully!"); 82 | 83 | Ok(()) 84 | } 85 | 86 | /// Utility function to print the decoded data. 87 | fn print_tuple(value: &DynSolValue, field_names: &[&str]) { 88 | if let DynSolValue::Tuple(values) = value { 89 | for (value, name) in values.iter().zip(field_names.iter()) { 90 | println!(" {name}: {value:?}"); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /examples/advanced/examples/encoding_sol_static.rs: -------------------------------------------------------------------------------- 1 | //! Example for static encoding calldata via `sol!`. 2 | 3 | use std::str::FromStr; 4 | 5 | use alloy::{ 6 | hex, 7 | primitives::{Address, U256}, 8 | sol, 9 | sol_types::SolCall, 10 | }; 11 | 12 | // Using UniswapV2 `swapExactTokensForTokens()` method for this example. 13 | // See: https://docs.uniswap.org/contracts/v2/reference/smart-contracts/router-02#swapexacttokensfortokens 14 | sol!( 15 | #[allow(missing_docs)] 16 | function swapExactTokensForTokens( 17 | uint256 amountIn, 18 | uint256 amountOutMin, 19 | address[] calldata path, 20 | address to, 21 | uint256 deadline 22 | ) external returns (uint256[] memory amounts); 23 | ); 24 | 25 | fn main() -> Result<(), Box> { 26 | // Swap 1 DAI for 1 USDC with a slippage tolerance of 1%. 27 | let amount_in = U256::from(1000000000000000000u128); // 1 token 28 | let amount_out_min = U256::from(9900000000000000000u128); // 0.99 tokens (1% slippage) 29 | 30 | // Construct path DAI --> WETH --> USDC. 31 | let token_in = Address::from_str("0x6B175474E89094C44Da98b954EedeAC495271d0F")?; // DAI 32 | let weth = Address::from_str("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")?; // WETH 33 | let token_out = Address::from_str("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")?; // USDC 34 | let path = vec![token_in, weth, token_out]; 35 | 36 | // Recipient of the output tokens. 37 | let to = Address::from_str("0x742d35Cc6634C0532925a3b844Bc454e4438f44e")?; 38 | 39 | // Unix timestamp after which the transaction will revert. 40 | let deadline = U256::from(1690000000u64); // Random timestamp 41 | 42 | let swap_data = 43 | swapExactTokensForTokensCall::new((amount_in, amount_out_min, path, to, deadline)); 44 | 45 | let encoded = hex::encode(swapExactTokensForTokensCall::abi_encode(&swap_data)); 46 | 47 | println!("Encoded: 0x{encoded}"); 48 | 49 | Ok(()) 50 | } 51 | -------------------------------------------------------------------------------- /examples/advanced/examples/reth_db_layer.rs: -------------------------------------------------------------------------------- 1 | //! `RethDbLayer` implementation to be used with `RethDbProvider` to wrap the Provider trait over 2 | //! reth-db. 3 | 4 | #![allow(dead_code)] 5 | use std::path::PathBuf; 6 | 7 | /// We use the tower-like layering functionality that has been baked into the alloy-provider to 8 | /// intercept the requests and redirect to the `RethDbProvider`. 9 | pub(crate) struct RethDbLayer { 10 | db_path: PathBuf, 11 | } 12 | 13 | /// Initialize the `RethDBLayer` with the path to the reth datadir. 14 | impl RethDbLayer { 15 | pub(crate) const fn new(db_path: PathBuf) -> Self { 16 | Self { db_path } 17 | } 18 | 19 | pub(crate) const fn db_path(&self) -> &PathBuf { 20 | &self.db_path 21 | } 22 | } 23 | 24 | const fn main() {} 25 | -------------------------------------------------------------------------------- /examples/advanced/examples/uniswap_u256/alloy_profit.rs: -------------------------------------------------------------------------------- 1 | //! Uniswap V2 Arbitrage Profit Calculation using alloy U256 2 | 3 | use alloy::primitives::utils::format_units; 4 | use eyre::Result; 5 | use uniswap_u256::helpers::alloy::{ 6 | get_amount_in, get_amount_out, get_sushi_pair, get_uniswap_pair, 7 | }; 8 | 9 | fn main() -> Result<()> { 10 | let uniswap_pair = get_uniswap_pair(); 11 | let sushi_pair = get_sushi_pair(); 12 | 13 | let amount_in = get_amount_in( 14 | uniswap_pair.reserve0, 15 | uniswap_pair.reserve1, 16 | false, 17 | sushi_pair.reserve0, 18 | sushi_pair.reserve1, 19 | ); 20 | 21 | let dai_amount_out = get_amount_out(uniswap_pair.reserve1, uniswap_pair.reserve0, amount_in); 22 | 23 | let weth_amount_out = get_amount_out(sushi_pair.reserve0, sushi_pair.reserve1, dai_amount_out); 24 | 25 | if weth_amount_out < amount_in { 26 | println!("No profit detected"); 27 | return Ok(()); 28 | } 29 | 30 | let profit = weth_amount_out - amount_in; 31 | println!("Alloy U256"); 32 | println!("WETH amount in {}", format_units(amount_in, 18).unwrap()); 33 | println!("WETH profit: {}", format_units(profit, 18).unwrap()); 34 | 35 | Ok(()) 36 | } 37 | -------------------------------------------------------------------------------- /examples/advanced/examples/uniswap_u256/ethers_profit.rs: -------------------------------------------------------------------------------- 1 | //! Uniswap V2 Arbitrage Profit Calculation using ethers-rs U256 2 | use eyre::Result; 3 | use uniswap_u256::helpers::ethers::{ 4 | display_token, get_amount_in, get_amount_out, get_sushi_pair, get_uniswap_pair, 5 | }; 6 | 7 | fn main() -> Result<()> { 8 | let uniswap_pair = get_uniswap_pair(); 9 | let sushi_pair = get_sushi_pair(); 10 | 11 | let amount_in = get_amount_in( 12 | uniswap_pair.reserve0, 13 | uniswap_pair.reserve1, 14 | false, 15 | sushi_pair.reserve0, 16 | sushi_pair.reserve1, 17 | ); 18 | 19 | let dai_amount_out = get_amount_out(uniswap_pair.reserve1, uniswap_pair.reserve0, amount_in); 20 | 21 | let weth_amount_out = get_amount_out(sushi_pair.reserve0, sushi_pair.reserve1, dai_amount_out); 22 | 23 | if weth_amount_out < amount_in { 24 | println!("No profit detected"); 25 | return Ok(()); 26 | } 27 | 28 | let profit = weth_amount_out - amount_in; 29 | println!("Ethers-rs U256"); 30 | println!("WETH amount in {}", display_token(amount_in)); 31 | println!("WETH profit: {}", display_token(profit)); 32 | 33 | Ok(()) 34 | } 35 | -------------------------------------------------------------------------------- /examples/advanced/examples/uniswap_u256/helpers/mod.rs: -------------------------------------------------------------------------------- 1 | /// Alloy helpers 2 | pub mod alloy; 3 | /// Ethers helpers 4 | pub mod ethers; 5 | -------------------------------------------------------------------------------- /examples/advanced/examples/uniswap_u256/lib.rs: -------------------------------------------------------------------------------- 1 | //! Alloy and ethers helpers 2 | 3 | /// Helpers 4 | pub mod helpers; 5 | -------------------------------------------------------------------------------- /examples/big-numbers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples-big-numbers" 3 | publish.workspace = true 4 | version.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dev-dependencies] 16 | alloy.workspace = true 17 | 18 | eyre.workspace = true 19 | -------------------------------------------------------------------------------- /examples/big-numbers/examples/comparison_equivalence.rs: -------------------------------------------------------------------------------- 1 | //! Example of comparison and equivalence of `U256` instances. 2 | 3 | use alloy::primitives::U256; 4 | use eyre::Result; 5 | 6 | /// `U256` implements traits in `std::cmp`, that means `U256` instances 7 | /// can be easily compared using standard Rust operators. 8 | fn main() -> Result<()> { 9 | // a == b 10 | let a = U256::from(100_u32); 11 | let b = U256::from(100_u32); 12 | assert!(a == b); 13 | 14 | // a < b 15 | let a = U256::from(1_u32); 16 | let b = U256::from(100_u32); 17 | assert!(a < b); 18 | 19 | // a <= b 20 | let a = U256::from(100_u32); 21 | let b = U256::from(100_u32); 22 | assert!(a <= b); 23 | 24 | // a > b 25 | let a = U256::from(100_u32); 26 | let b = U256::from(1_u32); 27 | assert!(a > b); 28 | 29 | // a >= b 30 | let a = U256::from(100_u32); 31 | let b = U256::from(100_u32); 32 | assert!(a >= b); 33 | 34 | // a == 0 35 | let a = U256::ZERO; 36 | assert!(a.is_zero()); 37 | 38 | Ok(()) 39 | } 40 | -------------------------------------------------------------------------------- /examples/big-numbers/examples/conversion.rs: -------------------------------------------------------------------------------- 1 | //! Example of converting `U256` to native Rust types. 2 | 3 | use alloy::primitives::{utils::format_units, U256}; 4 | use eyre::Result; 5 | 6 | /// `U256` provides useful conversion functions to enable transformation into native Rust types. 7 | /// 8 | /// It is important to note that converting a big-number to a floating point type (such as a `f32` 9 | /// or `f64`) can result in a loss of precision, since you cannot fit 256 bits of information into 10 | /// 64 bits. 11 | /// 12 | /// However, there may be cases where you want to perform conversions for presentation purposes. 13 | /// For example, you may want to display a large number to the user in a more readable format. 14 | fn main() -> Result<()> { 15 | let num = U256::from(42_u8); 16 | 17 | let a: u128 = num.to::(); 18 | assert_eq!(a, 42); 19 | 20 | let b: u64 = num.to::(); 21 | assert_eq!(b, 42); 22 | 23 | let c: u32 = num.to::(); 24 | assert_eq!(c, 42); 25 | 26 | let d: usize = num.to::(); 27 | assert_eq!(d, 42); 28 | 29 | let e: String = num.to_string(); 30 | assert_eq!(e, "42"); 31 | 32 | let f: String = format_units(num, 4)?; 33 | assert_eq!(f, "0.0042"); 34 | 35 | Ok(()) 36 | } 37 | -------------------------------------------------------------------------------- /examples/big-numbers/examples/create_instances.rs: -------------------------------------------------------------------------------- 1 | //! Example of creating instances of `U256` from strings and numbers. 2 | 3 | use std::str::FromStr; 4 | 5 | use alloy::primitives::{ 6 | utils::{parse_units, ParseUnits}, 7 | U256, 8 | }; 9 | use eyre::Result; 10 | 11 | fn main() -> Result<()> { 12 | // From strings 13 | let a = U256::from_str("42")?; 14 | assert_eq!(a.to_string(), "42"); 15 | 16 | let amount = "42"; 17 | let units = 4; 18 | let b: ParseUnits = parse_units(amount, units)?; 19 | assert_eq!(b.to_string(), "420000"); 20 | 21 | // From numbers 22 | let c = U256::from(42_u8); 23 | assert_eq!(c.to_string(), "42"); 24 | 25 | let d = U256::from(42_u16); 26 | assert_eq!(d.to_string(), "42"); 27 | 28 | let e = U256::from(42_u32); 29 | assert_eq!(e.to_string(), "42"); 30 | 31 | let f = U256::from(42_u64); 32 | assert_eq!(f.to_string(), "42"); 33 | 34 | let g = U256::from(42_u128); 35 | assert_eq!(g.to_string(), "42"); 36 | 37 | let h = U256::from(0x2a); 38 | assert_eq!(h.to_string(), "42"); 39 | 40 | let i = U256::from(42); 41 | assert_eq!(i.to_string(), "42"); 42 | 43 | Ok(()) 44 | } 45 | -------------------------------------------------------------------------------- /examples/big-numbers/examples/math_operations.rs: -------------------------------------------------------------------------------- 1 | //! Example of performing arithmetic operations with `U256`. 2 | 3 | use std::ops::{Div, Mul}; 4 | 5 | use alloy::primitives::{utils::format_units, U256}; 6 | use eyre::Result; 7 | 8 | /// `U256` implements traits in `std::ops`, that means it supports arithmetic operations 9 | /// using standard Rust operators `+`, `-`. `*`, `/`, `%`, along with additional utilities to 10 | /// perform common mathematical tasks. 11 | fn main() -> Result<()> { 12 | let a = U256::from(10); 13 | let b = U256::from(2); 14 | 15 | // addition 16 | let sum = a + b; 17 | assert_eq!(sum, U256::from(12)); 18 | 19 | // subtraction 20 | let difference = a - b; 21 | assert_eq!(difference, U256::from(8)); 22 | 23 | // multiplication 24 | let product = a * b; 25 | assert_eq!(product, U256::from(20)); 26 | 27 | // division 28 | let quotient = a / b; 29 | assert_eq!(quotient, U256::from(5)); 30 | 31 | // modulo 32 | let remainder = a % b; 33 | assert_eq!(remainder, U256::ZERO); // equivalent to `U256::from(0)` 34 | 35 | // exponentiation 36 | let power = a.pow(b); 37 | assert_eq!(power, U256::from(100)); 38 | 39 | // Multiply two 'ether' numbers: 40 | // Big numbers are integers, that can represent fixed point numbers. 41 | // For instance, 1 ether has 18 fixed 42 | // decimal places (1.000000000000000000), and its big number 43 | // representation is 10^18 = 1000000000000000000. 44 | // When we multiply such numbers we are summing up their exponents. 45 | // So if we multiply 10^18 * 10^18 we get 10^36, that is obviously incorrect. 46 | // In order to get the correct result we need to divide by 10^18. 47 | let eth1 = U256::from(10_000000000000000000_u128); // 10 ether 48 | let eth2 = U256::from(20_000000000000000000_u128); // 20 ether 49 | let base = U256::from(10).pow(U256::from(18)); 50 | let mul = eth1.mul(eth2).div(base); // We also divide by 10^18 51 | let s: String = format_units(mul, "ether")?; 52 | assert_eq!(s, "200.000000000000000000"); // 200 53 | 54 | Ok(()) 55 | } 56 | -------------------------------------------------------------------------------- /examples/big-numbers/examples/math_utilities.rs: -------------------------------------------------------------------------------- 1 | //! Example of using math utilities to handle big numbers in 'wei' units. 2 | 3 | use alloy::primitives::{ 4 | utils::{format_units, parse_units}, 5 | U256, 6 | }; 7 | use eyre::Result; 8 | 9 | fn main() -> Result<()> { 10 | parse_units_example()?; 11 | format_units_example()?; 12 | 13 | Ok(()) 14 | } 15 | 16 | /// dApps business logics handle big numbers in 'wei' units (i.e. sending transactions, on-chain 17 | /// math, etc.). We provide convenient methods to map user inputs (usually in 'ether' or 'gwei') 18 | /// into 'wei' format. 19 | fn parse_units_example() -> Result<()> { 20 | let pu = parse_units("1.0", "wei")?; 21 | let num: U256 = pu.into(); 22 | assert_eq!(num, U256::from(1)); 23 | 24 | let pu = parse_units("1.0", "kwei")?; 25 | let num: U256 = pu.into(); 26 | assert_eq!(num, U256::from(1000)); 27 | 28 | let pu = parse_units("1.0", "mwei")?; 29 | let num: U256 = pu.into(); 30 | assert_eq!(num, U256::from(1000000)); 31 | 32 | let pu = parse_units("1.0", "gwei")?; 33 | let num: U256 = pu.into(); 34 | assert_eq!(num, U256::from(1000000000)); 35 | 36 | let pu = parse_units("1.0", "szabo")?; 37 | let num: U256 = pu.into(); 38 | assert_eq!(num, U256::from(1000000000000_u128)); 39 | 40 | let pu = parse_units("1.0", "finney")?; 41 | let num: U256 = pu.into(); 42 | assert_eq!(num, U256::from(1000000000000000_u128)); 43 | 44 | let pu = parse_units("1.0", "ether")?; 45 | let num: U256 = pu.into(); 46 | assert_eq!(num, U256::from(1000000000000000000_u128)); 47 | 48 | let pu = parse_units("1.0", 18)?; 49 | let num: U256 = pu.into(); 50 | assert_eq!(num, U256::from(1000000000000000000_u128)); 51 | 52 | Ok(()) 53 | } 54 | 55 | /// dApps business logics handle big numbers in 'wei' units (i.e. sending transactions, on-chain 56 | /// math, etc.). On the other hand it is useful to convert big numbers into user readable formats 57 | /// when displaying on a UI. Generally dApps display numbers in 'ether' and 'gwei' units, 58 | /// respectively for displaying amounts and gas. The `format_units` function will format a big 59 | /// number into a user readable string. 60 | fn format_units_example() -> Result<()> { 61 | // 1 ETHER = 10^18 WEI 62 | let one_ether = U256::from(1000000000000000000_u128); 63 | 64 | let num: String = format_units(one_ether, "wei")?; 65 | assert_eq!(num, "1000000000000000000.0"); 66 | 67 | let num: String = format_units(one_ether, "gwei")?; 68 | assert_eq!(num, "1000000000.000000000"); 69 | 70 | let num: String = format_units(one_ether, "ether")?; 71 | assert_eq!(num, "1.000000000000000000"); 72 | 73 | // 1 GWEI = 10^9 WEI 74 | let one_gwei = U256::from(1000000000_u128); 75 | 76 | let num: String = format_units(one_gwei, 0)?; 77 | assert_eq!(num, "1000000000.0"); 78 | 79 | let num: String = format_units(one_gwei, "wei")?; 80 | assert_eq!(num, "1000000000.0"); 81 | 82 | let num: String = format_units(one_gwei, "kwei")?; 83 | assert_eq!(num, "1000000.000"); 84 | 85 | let num: String = format_units(one_gwei, "mwei")?; 86 | assert_eq!(num, "1000.000000"); 87 | 88 | let num: String = format_units(one_gwei, "gwei")?; 89 | assert_eq!(num, "1.000000000"); 90 | 91 | let num: String = format_units(one_gwei, "szabo")?; 92 | assert_eq!(num, "0.001000000000"); 93 | 94 | let num: String = format_units(one_gwei, "finney")?; 95 | assert_eq!(num, "0.000001000000000"); 96 | 97 | let num: String = format_units(one_gwei, "ether")?; 98 | assert_eq!(num, "0.000000001000000000"); 99 | 100 | Ok(()) 101 | } 102 | -------------------------------------------------------------------------------- /examples/comparison/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples-comparison" 3 | publish.workspace = true 4 | version.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dev-dependencies] 16 | alloy.workspace = true 17 | 18 | chrono = "0.4" 19 | clap = { version = "4.5", features = ["derive"] } 20 | eyre.workspace = true 21 | futures-util.workspace = true 22 | tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } 23 | -------------------------------------------------------------------------------- /examples/contracts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples-contracts" 3 | publish.workspace = true 4 | version.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dev-dependencies] 16 | alloy.workspace = true 17 | 18 | eyre.workspace = true 19 | tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } 20 | serde_json.workspace = true 21 | -------------------------------------------------------------------------------- /examples/contracts/examples/abi/Colors.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "colors", 5 | "inputs": [ 6 | { 7 | "name": "", 8 | "type": "address", 9 | "internalType": "address" 10 | } 11 | ], 12 | "outputs": [ 13 | { 14 | "name": "r", 15 | "type": "uint8", 16 | "internalType": "uint8" 17 | }, 18 | { 19 | "name": "g", 20 | "type": "uint8", 21 | "internalType": "uint8" 22 | }, 23 | { 24 | "name": "b", 25 | "type": "uint8", 26 | "internalType": "uint8" 27 | } 28 | ], 29 | "stateMutability": "view" 30 | }, 31 | { 32 | "type": "function", 33 | "name": "getColor", 34 | "inputs": [ 35 | { 36 | "name": "user", 37 | "type": "address", 38 | "internalType": "address" 39 | } 40 | ], 41 | "outputs": [ 42 | { 43 | "name": "", 44 | "type": "tuple", 45 | "internalType": "struct Colors.Color", 46 | "components": [ 47 | { 48 | "name": "r", 49 | "type": "uint8", 50 | "internalType": "uint8" 51 | }, 52 | { 53 | "name": "g", 54 | "type": "uint8", 55 | "internalType": "uint8" 56 | }, 57 | { 58 | "name": "b", 59 | "type": "uint8", 60 | "internalType": "uint8" 61 | } 62 | ] 63 | } 64 | ], 65 | "stateMutability": "view" 66 | }, 67 | { 68 | "type": "function", 69 | "name": "getColorAsTuple", 70 | "inputs": [ 71 | { 72 | "name": "user", 73 | "type": "address", 74 | "internalType": "address" 75 | } 76 | ], 77 | "outputs": [ 78 | { 79 | "name": "", 80 | "type": "uint8", 81 | "internalType": "uint8" 82 | }, 83 | { 84 | "name": "", 85 | "type": "uint8", 86 | "internalType": "uint8" 87 | }, 88 | { 89 | "name": "", 90 | "type": "uint8", 91 | "internalType": "uint8" 92 | } 93 | ], 94 | "stateMutability": "view" 95 | }, 96 | { 97 | "type": "function", 98 | "name": "setColor", 99 | "inputs": [ 100 | { 101 | "name": "r", 102 | "type": "uint8", 103 | "internalType": "uint8" 104 | }, 105 | { 106 | "name": "g", 107 | "type": "uint8", 108 | "internalType": "uint8" 109 | }, 110 | { 111 | "name": "b", 112 | "type": "uint8", 113 | "internalType": "uint8" 114 | } 115 | ], 116 | "outputs": [], 117 | "stateMutability": "nonpayable" 118 | } 119 | ] -------------------------------------------------------------------------------- /examples/contracts/examples/arb_profit_calc.rs: -------------------------------------------------------------------------------- 1 | //! Simple arbitrage profit calculator for WETH/DAI pools 2 | //! Reads the balaces of the Uniswap V2 and `Sushiswap` pools and calculates a basic arb 3 | //! opportunity. 4 | 5 | mod helpers; 6 | use crate::helpers::{get_amount_in, get_amount_out, get_sushi_pair, get_uniswap_pair}; 7 | use alloy::primitives::utils::format_units; 8 | use eyre::Result; 9 | 10 | fn main() -> Result<()> { 11 | // Get the pool contract interfaces 12 | let uniswap_pair = get_uniswap_pair(); 13 | let sushi_pair = get_sushi_pair(); 14 | 15 | let amount_in = get_amount_in( 16 | uniswap_pair.reserve0, 17 | uniswap_pair.reserve1, 18 | false, 19 | sushi_pair.reserve0, 20 | sushi_pair.reserve1, 21 | ); 22 | 23 | let dai_amount_out = get_amount_out(uniswap_pair.reserve1, uniswap_pair.reserve0, amount_in); 24 | let weth_amount_out = get_amount_out(sushi_pair.reserve0, sushi_pair.reserve1, dai_amount_out); 25 | 26 | if weth_amount_out < amount_in { 27 | println!("No profit detected"); 28 | return Ok(()); 29 | } 30 | 31 | let profit = weth_amount_out - amount_in; 32 | println!("Alloy U256"); 33 | println!("WETH amount in {}", format_units(amount_in, 18).unwrap()); 34 | println!("WETH profit: {}", format_units(profit, 18).unwrap()); 35 | 36 | Ok(()) 37 | } 38 | -------------------------------------------------------------------------------- /examples/contracts/examples/artifacts/FlashBotsMultiCall.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | interface IERC20 { 7 | event Approval(address indexed owner, address indexed spender, uint value); 8 | event Transfer(address indexed from, address indexed to, uint value); 9 | 10 | function name() external view returns (string memory); 11 | function symbol() external view returns (string memory); 12 | function decimals() external view returns (uint8); 13 | function totalSupply() external view returns (uint); 14 | function balanceOf(address owner) external view returns (uint); 15 | function allowance( 16 | address owner, 17 | address spender 18 | ) external view returns (uint); 19 | 20 | function approve(address spender, uint value) external returns (bool); 21 | function transfer(address to, uint value) external returns (bool); 22 | function transferFrom( 23 | address from, 24 | address to, 25 | uint value 26 | ) external returns (bool); 27 | } 28 | 29 | interface IWETH is IERC20 { 30 | function deposit() external payable; 31 | function withdraw(uint) external; 32 | } 33 | 34 | // This contract simply calls multiple targets sequentially, ensuring WETH balance before and after 35 | 36 | contract FlashBotsMultiCall { 37 | address private immutable owner; 38 | address private immutable executor; 39 | IWETH private constant WETH = 40 | IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 41 | 42 | modifier onlyExecutor() { 43 | require(msg.sender == executor); 44 | _; 45 | } 46 | 47 | modifier onlyOwner() { 48 | require(msg.sender == owner); 49 | _; 50 | } 51 | 52 | constructor(address _executor) payable { 53 | owner = msg.sender; 54 | executor = _executor; 55 | if (msg.value > 0) { 56 | WETH.deposit{value: msg.value}(); 57 | } 58 | } 59 | 60 | receive() external payable {} 61 | 62 | function uniswapWeth( 63 | uint256 _wethAmountToFirstMarket, 64 | uint256 _ethAmountToCoinbase, 65 | address[] memory _targets, 66 | bytes[] memory _payloads 67 | ) external payable onlyExecutor { 68 | require(_targets.length == _payloads.length); 69 | uint256 _wethBalanceBefore = WETH.balanceOf(address(this)); 70 | WETH.transfer(_targets[0], _wethAmountToFirstMarket); 71 | for (uint256 i = 0; i < _targets.length; i++) { 72 | (bool _success, bytes memory _response) = _targets[i].call( 73 | _payloads[i] 74 | ); 75 | require(_success); 76 | _response; 77 | } 78 | 79 | uint256 _wethBalanceAfter = WETH.balanceOf(address(this)); 80 | require(_wethBalanceAfter > _wethBalanceBefore + _ethAmountToCoinbase); 81 | if (_ethAmountToCoinbase == 0) return; 82 | 83 | uint256 _ethBalance = address(this).balance; 84 | if (_ethBalance < _ethAmountToCoinbase) { 85 | WETH.withdraw(_ethAmountToCoinbase - _ethBalance); 86 | } 87 | block.coinbase.transfer(_ethAmountToCoinbase); 88 | } 89 | 90 | function call( 91 | address payable _to, 92 | uint256 _value, 93 | bytes calldata _data 94 | ) external payable onlyOwner returns (bytes memory) { 95 | require(_to != address(0)); 96 | (bool _success, bytes memory _result) = _to.call{value: _value}(_data); 97 | require(_success); 98 | return _result; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /examples/contracts/examples/deploy_from_artifact.rs: -------------------------------------------------------------------------------- 1 | //! Example of deploying a contract from an artifact using the `sol!` macro to Anvil and interacting 2 | //! with it. 3 | 4 | use alloy::{primitives::U256, providers::ProviderBuilder, sol}; 5 | use eyre::Result; 6 | 7 | // Codegen from artifact. 8 | sol!( 9 | #[allow(missing_docs)] 10 | #[sol(rpc)] 11 | Counter, 12 | "examples/artifacts/Counter.json" 13 | ); 14 | 15 | #[tokio::main] 16 | async fn main() -> Result<()> { 17 | // Spin up a local Anvil node. 18 | // Ensure `anvil` is available in $PATH. 19 | let provider = ProviderBuilder::new().connect_anvil_with_wallet(); 20 | 21 | // Deploy the `Counter` contract. 22 | let contract = Counter::deploy(&provider).await?; 23 | 24 | println!("Deployed contract at address: {}", contract.address()); 25 | 26 | // Set the number to 42. 27 | let builder = contract.setNumber(U256::from(42)); 28 | let tx_hash = builder.send().await?.watch().await?; 29 | 30 | println!("Set number to 42: {tx_hash}"); 31 | 32 | // Increment the number to 43. 33 | let builder = contract.increment(); 34 | let tx_hash = builder.send().await?.watch().await?; 35 | 36 | println!("Incremented number: {tx_hash}"); 37 | 38 | // Retrieve the number, which should be 43. 39 | let builder = contract.number(); 40 | 41 | // Note: because the artifact generated by `solc` does not include named return values it is 42 | // not possible to derive the return value name `number` from the artifact. This means that the 43 | // return value must be accessed by index - as if it is an unnamed value. 44 | // If you prefer to use named return values, it is recommended to embed the Solidity code 45 | // directly in the `sol!` macro as shown in `deploy_from_contract.rs`. 46 | let number = builder.call().await?; 47 | 48 | println!("Retrieved number: {number}"); 49 | 50 | Ok(()) 51 | } 52 | -------------------------------------------------------------------------------- /examples/contracts/examples/deploy_from_bytecode.rs: -------------------------------------------------------------------------------- 1 | //! Example of deploying a contract at runtime from Solidity bytecode to Anvil and interacting with 2 | //! it. 3 | 4 | use alloy::{ 5 | hex, 6 | network::{ReceiptResponse, TransactionBuilder}, 7 | primitives::U256, 8 | providers::{Provider, ProviderBuilder}, 9 | rpc::types::TransactionRequest, 10 | sol, 11 | }; 12 | use eyre::Result; 13 | 14 | // If you have the bytecode known at build time, use the `deploy_from_contract` example. 15 | // This method benefits from using bytecode at runtime, e.g., from newly deployed contracts, to 16 | // analyze the behavior. 17 | sol! { 18 | #[allow(missing_docs)] 19 | #[sol(rpc)] 20 | contract Counter { 21 | uint256 public number; 22 | 23 | function setNumber(uint256 newNumber) public { 24 | number = newNumber; 25 | } 26 | 27 | function increment() public { 28 | number++; 29 | } 30 | } 31 | } 32 | 33 | #[tokio::main] 34 | async fn main() -> Result<()> { 35 | // Spin up a local Anvil node. 36 | // Ensure `anvil` is available in $PATH. 37 | let provider = ProviderBuilder::new().connect_anvil_with_wallet(); 38 | 39 | // Deploy the `Counter` contract from bytecode at runtime. 40 | let bytecode = hex::decode( 41 | // solc v0.8.26; solc Counter.sol --via-ir --optimize --bin 42 | "6080806040523460135760df908160198239f35b600080fdfe6080806040526004361015601257600080fd5b60003560e01c9081633fb5c1cb1460925781638381f58a146079575063d09de08a14603c57600080fd5b3460745760003660031901126074576000546000198114605e57600101600055005b634e487b7160e01b600052601160045260246000fd5b600080fd5b3460745760003660031901126074576020906000548152f35b34607457602036600319011260745760043560005500fea2646970667358221220e978270883b7baed10810c4079c941512e93a7ba1cd1108c781d4bc738d9090564736f6c634300081a0033" 43 | )?; 44 | let tx = TransactionRequest::default().with_deploy_code(bytecode); 45 | 46 | // Deploy the contract. 47 | let receipt = provider.send_transaction(tx).await?.get_receipt().await?; 48 | 49 | let contract_address = receipt.contract_address().expect("Failed to get contract address"); 50 | let contract = Counter::new(contract_address, &provider); 51 | println!("Deployed contract at address: {}", contract.address()); 52 | 53 | // Set number 54 | let builder = contract.setNumber(U256::from(42)); 55 | let tx_hash = builder.send().await?.watch().await?; 56 | 57 | println!("Set number to 42: {tx_hash}"); 58 | 59 | // Increment the number to 43. 60 | let builder = contract.increment(); 61 | let tx_hash = builder.send().await?.watch().await?; 62 | 63 | println!("Incremented number: {tx_hash}"); 64 | 65 | // Retrieve the number, which should be 43. 66 | let builder = contract.number(); 67 | let number = builder.call().await?; 68 | 69 | println!("Retrieved number: {number}"); 70 | 71 | Ok(()) 72 | } 73 | -------------------------------------------------------------------------------- /examples/contracts/examples/deploy_from_contract.rs: -------------------------------------------------------------------------------- 1 | //! Example of deploying a contract from Solidity code using the `sol!` macro to Anvil and 2 | //! interacting with it. 3 | 4 | use alloy::{primitives::U256, providers::ProviderBuilder, sol}; 5 | use eyre::Result; 6 | 7 | // Codegen from embedded Solidity code and precompiled bytecode. 8 | sol! { 9 | #[allow(missing_docs)] 10 | // solc v0.8.26; solc Counter.sol --via-ir --optimize --bin 11 | #[sol(rpc, bytecode="6080806040523460135760df908160198239f35b600080fdfe6080806040526004361015601257600080fd5b60003560e01c9081633fb5c1cb1460925781638381f58a146079575063d09de08a14603c57600080fd5b3460745760003660031901126074576000546000198114605e57600101600055005b634e487b7160e01b600052601160045260246000fd5b600080fd5b3460745760003660031901126074576020906000548152f35b34607457602036600319011260745760043560005500fea2646970667358221220e978270883b7baed10810c4079c941512e93a7ba1cd1108c781d4bc738d9090564736f6c634300081a0033")] 12 | contract Counter { 13 | uint256 public number; 14 | 15 | function setNumber(uint256 newNumber) public { 16 | number = newNumber; 17 | } 18 | 19 | function increment() public { 20 | number++; 21 | } 22 | } 23 | } 24 | 25 | #[tokio::main] 26 | async fn main() -> Result<()> { 27 | // Spin up a local Anvil node. 28 | // Ensure `anvil` is available in $PATH. 29 | let provider = ProviderBuilder::new().connect_anvil_with_wallet(); 30 | 31 | // Deploy the `Counter` contract. 32 | let contract = Counter::deploy(&provider).await?; 33 | 34 | println!("Deployed contract at address: {}", contract.address()); 35 | 36 | let builder = contract.setNumber(U256::from(42)); 37 | let tx_hash = builder.send().await?.watch().await?; 38 | 39 | println!("Set number to 42: {tx_hash}"); 40 | 41 | // Increment the number to 43. 42 | let builder = contract.increment(); 43 | let tx_hash = builder.send().await?.watch().await?; 44 | 45 | println!("Incremented number: {tx_hash}"); 46 | 47 | // Retrieve the number, which should be 43. 48 | let builder = contract.number(); 49 | let number = builder.call().await?; 50 | 51 | println!("Retrieved number: {number}"); 52 | 53 | Ok(()) 54 | } 55 | -------------------------------------------------------------------------------- /examples/contracts/examples/interact_with_abi.rs: -------------------------------------------------------------------------------- 1 | //! Example of generating code from ABI file using the `sol!` macro to interact with the contract. 2 | 3 | use alloy::{primitives::address, providers::ProviderBuilder, sol}; 4 | use eyre::Result; 5 | 6 | // Codegen from ABI file to interact with the contract. 7 | sol!( 8 | #[allow(missing_docs)] 9 | #[sol(rpc)] 10 | IWETH9, 11 | "examples/abi/IWETH9.json" 12 | ); 13 | 14 | #[tokio::main] 15 | async fn main() -> Result<()> { 16 | // Spin up a forked Anvil node. 17 | // Ensure `anvil` is available in $PATH. 18 | let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc"; 19 | let provider = 20 | ProviderBuilder::new().connect_anvil_with_wallet_and_config(|anvil| anvil.fork(rpc_url))?; 21 | 22 | // Create a contract instance. 23 | let contract = IWETH9::new(address!("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"), provider); 24 | 25 | // Call the contract, retrieve the total supply. 26 | let total_supply = contract.totalSupply().call().await?; 27 | 28 | println!("WETH total supply is {total_supply}"); 29 | 30 | Ok(()) 31 | } 32 | -------------------------------------------------------------------------------- /examples/contracts/examples/jsonrpc_error_decoding.rs: -------------------------------------------------------------------------------- 1 | //! This example demonstrates how to decode a custom JSON RPC error. 2 | 3 | use alloy::{primitives::U256, rpc::json_rpc::ErrorPayload, sol}; 4 | use eyre::Result; 5 | 6 | // Define a custom error using the sol! macro. 7 | sol! { 8 | #[allow(missing_docs)] 9 | library Errors { 10 | error SomeCustomError(uint256 a); 11 | } 12 | } 13 | 14 | fn main() -> Result<()> { 15 | // Sample JSON error payload from an Ethereum JSON RPC response. 16 | let json = r#"{"code":3,"message":"execution reverted: ","data":"0x810f00230000000000000000000000000000000000000000000000000000000000000001"}"#; 17 | 18 | // Parse the JSON into an `ErrorPayload` struct. 19 | let payload: ErrorPayload = serde_json::from_str(json)?; 20 | 21 | // Attempt to decode the error payload as our custom error. 22 | let Errors::SomeCustomError { a } = 23 | payload.as_decoded_error::().unwrap(); 24 | 25 | assert_eq!(a, U256::from(1)); 26 | 27 | Ok(()) 28 | } 29 | -------------------------------------------------------------------------------- /examples/contracts/examples/revert_decoding.rs: -------------------------------------------------------------------------------- 1 | //! This example demonstrates how to decode revert data into a custom error. 2 | 3 | use alloy::{primitives::U256, providers::ProviderBuilder, sol}; 4 | use eyre::Result; 5 | use Errors::{ErrorsErrors, SomeCustomError}; 6 | 7 | // Define a custom error using the sol! macro. 8 | sol! { 9 | // solc: 0.8.25; solc DecodingRevert.sol --optimize --bin 10 | #[allow(missing_docs)] 11 | #[derive(Debug, PartialEq, Eq)] 12 | library Errors { 13 | error SomeCustomError(uint256 a); 14 | error AnotherError(uint64 b); 15 | } 16 | 17 | #[derive(Debug)] 18 | #[sol(rpc, bytecode = "6080604052348015600e575f80fd5b5060a780601a5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c8063b48fb6cf14602a575b5f80fd5b60396035366004605b565b603b565b005b60405163810f002360e01b81526004810182905260240160405180910390fd5b5f60208284031215606a575f80fd5b503591905056fea26469706673582212200898a6b7d5b1bcc62a40abf2470704fe9c6cd850c77b0654134fc0ecbf0d5e6f64736f6c63430008190033")] 19 | contract ThrowsError { 20 | function error(uint256 a) external { 21 | revert Errors.SomeCustomError(a); 22 | } 23 | } 24 | } 25 | 26 | #[tokio::main] 27 | async fn main() -> Result<()> { 28 | // Setup an Anvil provider with a wallet. 29 | // Make sure `anvil` is in your $PATH. 30 | let provider = ProviderBuilder::new().connect_anvil_with_wallet(); 31 | 32 | // Deploy the contract. 33 | let contract = ThrowsError::deploy(&provider).await?; 34 | 35 | // Call the `error` function which will revert with a custom error. 36 | let err = contract.error(U256::from(1)).call().await.unwrap_err(); 37 | 38 | // Get the raw bytes of the revert data. 39 | let revert_data = err.as_revert_data().unwrap(); 40 | 41 | println!("Decoding revert data: {revert_data:?}"); 42 | 43 | // Decode the revert data as a custom error. 44 | let decoded_err = err.as_decoded_error::().unwrap(); 45 | 46 | println!("Decoded as: {decoded_err:?}"); 47 | 48 | assert_eq!(decoded_err, SomeCustomError { a: U256::from(1) }); 49 | 50 | // At times you may not be sure which error is being returned by the function. 51 | // In such cases, we can try to decode the revert over all the possible errors provieded to the 52 | // the sol! macro. 53 | let decoded_err = err.as_decoded_interface_error::().unwrap(); 54 | 55 | // The above returns an enum with the errors as its variants. 56 | match decoded_err { 57 | ErrorsErrors::SomeCustomError(err) => { 58 | println!("Decoded as: {err:?}"); 59 | assert_eq!(err.a, U256::from(1)); 60 | } 61 | ErrorsErrors::AnotherError(err) => { 62 | println!("Decoded as: {err:?}"); 63 | assert_eq!(err.b, 0); 64 | } 65 | } 66 | 67 | Ok(()) 68 | } 69 | -------------------------------------------------------------------------------- /examples/fillers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples-fillers" 3 | publish.workspace = true 4 | version.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dev-dependencies] 16 | alloy.workspace = true 17 | 18 | eyre.workspace = true 19 | serde_json.workspace = true 20 | tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } 21 | reqwest = "0.12.15" 22 | -------------------------------------------------------------------------------- /examples/fillers/examples/gas_filler.rs: -------------------------------------------------------------------------------- 1 | //! Example of using the `GasFiller` in the provider. 2 | 3 | use alloy::{ 4 | consensus::Transaction, 5 | network::TransactionBuilder, 6 | primitives::{address, U256}, 7 | providers::{Provider, ProviderBuilder}, 8 | rpc::types::request::TransactionRequest, 9 | }; 10 | use eyre::Result; 11 | 12 | #[tokio::main] 13 | async fn main() -> Result<()> { 14 | // Spin up a local Anvil node. 15 | // Ensure `anvil` is available in $PATH. 16 | let provider = ProviderBuilder::new() 17 | // You can disable the recommended fillers by calling the `disable_recommended_fillers()` 18 | // and pick the fillers of your choice. 19 | .disable_recommended_fillers() 20 | // Add the `GasFiller` to the provider. 21 | // It is generally recommended to use the recommended fillers which includes the GasFiller, 22 | // enabled by building the provider using ProviderBuilder::new(). 23 | .with_gas_estimation() 24 | .connect_anvil_with_wallet(); 25 | 26 | // Build an EIP-1559 type transaction to send 100 wei to Vitalik. 27 | let vitalik = address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"); 28 | let tx = TransactionRequest::default() 29 | .with_to(vitalik) 30 | .with_value(U256::from(100)) 31 | // Notice that without the `NonceFiller`, you need to set `nonce` field. 32 | .with_nonce(0) 33 | // Notice that without the `ChainIdFiller`, you need to set the `chain_id` field. 34 | .with_chain_id(provider.get_chain_id().await?); 35 | 36 | // Send the transaction, the nonce (0) is automatically managed by the provider. 37 | let builder = provider.send_transaction(tx.clone()).await?; 38 | let node_hash = *builder.tx_hash(); 39 | let pending_tx = 40 | provider.get_transaction_by_hash(node_hash).await?.expect("Pending transaction not found"); 41 | assert_eq!(pending_tx.nonce(), 0); 42 | 43 | println!("Transaction sent with nonce: {}", pending_tx.nonce()); 44 | 45 | // Update the nonce and send the transaction again. 46 | let tx = tx.with_nonce(1); 47 | 48 | // Send the transaction, the nonce (1) is automatically managed by the provider. 49 | let builder = provider.send_transaction(tx).await?; 50 | let node_hash = *builder.tx_hash(); 51 | let pending_tx = 52 | provider.get_transaction_by_hash(node_hash).await?.expect("Pending transaction not found"); 53 | assert_eq!(pending_tx.nonce(), 1); 54 | 55 | println!("Transaction sent with nonce: {}", pending_tx.nonce()); 56 | 57 | Ok(()) 58 | } 59 | -------------------------------------------------------------------------------- /examples/fillers/examples/recommended_fillers.rs: -------------------------------------------------------------------------------- 1 | //! Example of using the `.with_recommended_fillers()` method in the provider. 2 | 3 | use alloy::{ 4 | consensus::Transaction, 5 | network::TransactionBuilder, 6 | primitives::{address, U256}, 7 | providers::{Provider, ProviderBuilder}, 8 | rpc::types::request::TransactionRequest, 9 | }; 10 | use eyre::Result; 11 | 12 | #[tokio::main] 13 | async fn main() -> Result<()> { 14 | // Spin up a local Anvil node. 15 | // Ensure `anvil` is available in $PATH. 16 | // After `alloy 0.11`, the recommended fillers are enabled by default when building the provider 17 | // with `ProviderBuilder::new()`. 18 | let provider = ProviderBuilder::new() 19 | // Adds the `ChainIdFiller`, `GasFiller` and the `NonceFiller` layers. 20 | // This is the recommended way to set up the provider. 21 | // One can disable the recommended fillers by calling the `disable_recommended_fillers()` 22 | // method or building the provider with `ProviderBuilder::default()`. 23 | .connect_anvil_with_wallet(); 24 | 25 | // Build an EIP-1559 type transaction to send 100 wei to Vitalik. 26 | // Notice that the `nonce` field is set by the `NonceFiller`. 27 | // Notice that the gas related fields are set by the `GasFiller`. 28 | // Notice that the `chain_id` field is set by the `ChainIdFiller`. 29 | let vitalik = address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"); 30 | let tx = TransactionRequest::default().with_to(vitalik).with_value(U256::from(100)); 31 | 32 | // Send the transaction, the nonce (0) is automatically managed by the provider. 33 | let builder = provider.send_transaction(tx.clone()).await?; 34 | let node_hash = *builder.tx_hash(); 35 | let pending_tx = 36 | provider.get_transaction_by_hash(node_hash).await?.expect("Pending transaction not found"); 37 | assert_eq!(pending_tx.nonce(), 0); 38 | 39 | println!("Transaction sent with nonce: {}", pending_tx.nonce()); 40 | 41 | // Send the transaction, the nonce (1) is automatically managed by the provider. 42 | let builder = provider.send_transaction(tx).await?; 43 | let node_hash = *builder.tx_hash(); 44 | let pending_tx = 45 | provider.get_transaction_by_hash(node_hash).await?.expect("Pending transaction not found"); 46 | assert_eq!(pending_tx.nonce(), 1); 47 | 48 | println!("Transaction sent with nonce: {}", pending_tx.nonce()); 49 | 50 | Ok(()) 51 | } 52 | -------------------------------------------------------------------------------- /examples/fillers/examples/wallet_filler.rs: -------------------------------------------------------------------------------- 1 | //! Example of using the `WalletFiller` in the provider. 2 | 3 | use alloy::{ 4 | network::TransactionBuilder, 5 | node_bindings::Anvil, 6 | primitives::{address, b256, U256}, 7 | providers::{Provider, ProviderBuilder}, 8 | rpc::types::request::TransactionRequest, 9 | signers::local::PrivateKeySigner, 10 | }; 11 | use eyre::Result; 12 | 13 | #[tokio::main] 14 | async fn main() -> Result<()> { 15 | // Spin up a local Anvil node. 16 | // Ensure `anvil` is available in $PATH. 17 | let anvil = Anvil::new().try_spawn()?; 18 | 19 | // Set up signer from the first default Anvil account (Alice). 20 | let signer: PrivateKeySigner = anvil.keys()[0].clone().into(); 21 | 22 | // Create a provider with the wallet. 23 | let rpc_url = anvil.endpoint_url(); 24 | let provider = ProviderBuilder::new() 25 | // Add the `WalletFiller` to the provider 26 | .wallet(signer) 27 | .connect_http(rpc_url); 28 | 29 | // Build an EIP-1559 type transaction to send 100 wei to Vitalik. 30 | let vitalik = address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"); 31 | let tx = TransactionRequest::default() 32 | .with_to(vitalik) 33 | .with_value(U256::from(100)) 34 | // Notice that without the `ChainIdFiller`, you need to set the `chain_id` field. 35 | .with_chain_id(provider.get_chain_id().await?) 36 | // Notice that without the `NonceFiller`, you need to manually set the nonce field. 37 | .with_nonce(0) 38 | // Notice that without the `GasFiller`, you need to set the gas related fields. 39 | .max_fee_per_gas(20_000_000_000) 40 | .max_priority_fee_per_gas(1_000_000_000) 41 | .with_gas_limit(21_000); 42 | 43 | let builder = provider.send_transaction(tx).await?; 44 | let node_hash = *builder.tx_hash(); 45 | 46 | println!( 47 | "Node hash matches expected hash: {}", 48 | node_hash == b256!("eb56033eab0279c6e9b685a5ec55ea0ff8d06056b62b7f36974898d4fbb57e64") 49 | ); 50 | 51 | // Send the transaction and wait for the broadcast. 52 | let pending_tx = builder.register().await?; 53 | 54 | println!("Pending transaction hash matches node hash: {}", *pending_tx.tx_hash() == node_hash); 55 | 56 | let tx_hash = pending_tx.await?; 57 | assert_eq!(tx_hash, node_hash); 58 | 59 | println!("Transaction hash matches node hash: {}", tx_hash == node_hash); 60 | 61 | // Wait for the transaction to be included and get the receipt. 62 | let receipt = 63 | provider.get_transaction_receipt(tx_hash).await?.expect("Transaction receipt not found"); 64 | let receipt_hash = receipt.transaction_hash; 65 | assert_eq!(receipt_hash, node_hash); 66 | 67 | println!("Transaction receipt hash matches node hash: {}", receipt_hash == node_hash); 68 | 69 | Ok(()) 70 | } 71 | -------------------------------------------------------------------------------- /examples/layers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples-layers" 3 | publish.workspace = true 4 | version.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dev-dependencies] 16 | alloy = { workspace = true, features = ["hyper"] } 17 | 18 | eyre.workspace = true 19 | tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } 20 | tower = { version = "0.5", features = ["retry"] } 21 | http-body-util = "0.1" 22 | tracing-subscriber = "0.3" 23 | -------------------------------------------------------------------------------- /examples/layers/examples/delay_layer.rs: -------------------------------------------------------------------------------- 1 | //! Demonstrates how to implement a custom transport layer that delays dispatching the requests. 2 | 3 | use eyre::Result; 4 | use std::{ 5 | task::{Context, Poll}, 6 | time::Duration, 7 | }; 8 | 9 | use alloy::{ 10 | network::TransactionBuilder, 11 | node_bindings::Anvil, 12 | primitives::{Address, U256}, 13 | providers::{Provider, ProviderBuilder}, 14 | rpc::{client::ClientBuilder, types::TransactionRequest}, 15 | signers::local::PrivateKeySigner, 16 | transports::BoxFuture, 17 | }; 18 | use tokio::time::sleep; 19 | use tower::{Layer, Service}; 20 | 21 | /// A [`tower::Service`] that delays the dispatch of requests by a specified duration. 22 | #[derive(Debug, Clone)] 23 | pub struct DelayService { 24 | service: S, 25 | delay: Duration, 26 | } 27 | 28 | /// A [`tower::Layer`] that returns a new [`DelayService`] with the specified delay. 29 | #[derive(Debug, Clone)] 30 | pub struct DelayLayer { 31 | delay: Duration, 32 | } 33 | 34 | impl DelayLayer { 35 | /// Creates a new [`DelayLayer`] with the specified delay. 36 | pub const fn new(delay: Duration) -> Self { 37 | Self { delay } 38 | } 39 | } 40 | 41 | impl Layer for DelayLayer { 42 | type Service = DelayService; 43 | 44 | fn layer(&self, service: S) -> Self::Service { 45 | DelayService { service, delay: self.delay } 46 | } 47 | } 48 | 49 | /// Implement the [`tower::Service`] trait for the [`DelayService`]. 50 | impl Service for DelayService 51 | where 52 | S: Service + Send, 53 | S::Future: Send + 'static, 54 | { 55 | type Response = S::Response; 56 | type Error = S::Error; 57 | type Future = BoxFuture<'static, Result>; 58 | 59 | fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { 60 | self.service.poll_ready(cx) 61 | } 62 | 63 | fn call(&mut self, req: Request) -> Self::Future { 64 | let delay = self.delay; 65 | let future = self.service.call(req); 66 | 67 | Box::pin(async move { 68 | println!("Delaying for {} seconds...", delay.as_secs()); 69 | sleep(delay).await; 70 | println!("Dispatching request..."); 71 | future.await 72 | }) 73 | } 74 | } 75 | 76 | #[tokio::main] 77 | async fn main() -> Result<()> { 78 | let anvil = Anvil::new().try_spawn()?; 79 | let signer: PrivateKeySigner = anvil.keys()[0].clone().into(); 80 | 81 | // Build a RPC client with the `DelayLayer`. 82 | let client = ClientBuilder::default() 83 | .layer(DelayLayer::new(Duration::from_secs(1))) 84 | .http(anvil.endpoint().parse()?); 85 | 86 | // Instatiate a provider with the RPC-client that uses the `DelayLayer`. 87 | let provider = ProviderBuilder::new().wallet(signer).connect_client(client); 88 | 89 | let bob = Address::from([0x42; 20]); 90 | let tx = TransactionRequest::default().with_to(bob).with_value(U256::from(1)); 91 | 92 | let bob_balance_before = provider.get_balance(bob).await?; 93 | let receipt = provider.send_transaction(tx).await?.get_receipt().await?; 94 | assert!(receipt.status(), "Transaction failed"); 95 | let bob_balance_after = provider.get_balance(bob).await?; 96 | println!("Balance before: {bob_balance_before}\nBalance after: {bob_balance_after}"); 97 | 98 | Ok(()) 99 | } 100 | -------------------------------------------------------------------------------- /examples/layers/examples/fallback_layer.rs: -------------------------------------------------------------------------------- 1 | //! Test the fallback layer provider. 2 | 3 | use std::{num::NonZeroUsize, time::Duration}; 4 | 5 | use alloy::{ 6 | providers::{Provider, ProviderBuilder}, 7 | rpc::client::RpcClient, 8 | transports::{ 9 | http::{reqwest::Url, Http}, 10 | layers::FallbackLayer, 11 | }, 12 | }; 13 | use eyre::Result; 14 | use tower::ServiceBuilder; 15 | 16 | #[tokio::main] 17 | async fn main() -> Result<()> { 18 | let _ = tracing_subscriber::fmt::try_init(); 19 | 20 | // Configure the fallback layer 21 | let fallback_layer = 22 | FallbackLayer::default().with_active_transport_count(NonZeroUsize::new(3).unwrap()); 23 | 24 | // Define your list of transports to use 25 | let transports = vec![ 26 | Http::new(Url::parse("https://reth-ethereum.ithaca.xyz/rpc")?), 27 | Http::new(Url::parse("https://eth.llamarpc.com")?), 28 | Http::new(Url::parse("https://ethereum-rpc.publicnode.com")?), 29 | ]; 30 | 31 | // Apply the FallbackLayer to the transports 32 | let transport = ServiceBuilder::new().layer(fallback_layer).service(transports); 33 | let client = RpcClient::builder().transport(transport, false); 34 | let provider = ProviderBuilder::new().connect_client(client); 35 | 36 | // Get the latest block number using the provider with ranked transports. 37 | // This will also print the rankings of the transports to the console. 38 | let max = 10; 39 | let mut count = 0; 40 | loop { 41 | let latest_block = provider.get_block_number().await?; 42 | println!("Latest block number: {latest_block}"); 43 | tokio::time::sleep(Duration::from_secs(1)).await; 44 | 45 | count += 1; 46 | if count >= max { 47 | break; 48 | } 49 | } 50 | 51 | Ok(()) 52 | } 53 | -------------------------------------------------------------------------------- /examples/layers/examples/hyper_http_layer.rs: -------------------------------------------------------------------------------- 1 | //! This example demonstrates how to write a custom layer for the [`hyper`] HTTP client that can 2 | //! modify the underlying HTTP request before it is sent. 3 | 4 | use alloy::{ 5 | node_bindings::Anvil, 6 | providers::{Provider, ProviderBuilder}, 7 | rpc::client::RpcClient, 8 | transports::http::{ 9 | hyper, 10 | hyper_util::{ 11 | client::legacy::{Client, Error}, 12 | rt::TokioExecutor, 13 | }, 14 | Http, HyperClient, HyperResponse, HyperResponseFut, 15 | }, 16 | }; 17 | use eyre::Result; 18 | use http_body_util::Full; 19 | use tower::{Layer, Service}; 20 | 21 | #[tokio::main] 22 | async fn main() -> Result<()> { 23 | // Start an Anvil node. 24 | let anvil = Anvil::new().spawn(); 25 | 26 | // Create a new Hyper client. 27 | let hyper_client = 28 | Client::builder(TokioExecutor::new()).build_http::>(); 29 | 30 | // Use tower::ServiceBuilder to stack layers on top of the Hyper client. 31 | let service = tower::ServiceBuilder::new().layer(RequestModifyingLayer).service(hyper_client); 32 | 33 | // Instantiate the HyperClient with the stacked layers. 34 | let layer_transport = HyperClient::, _>::with_service(service); 35 | let http = Http::with_client(layer_transport, anvil.endpoint_url()); 36 | 37 | // Create a new RPC client with the Hyper transport. 38 | let rpc_client = RpcClient::new(http, true); 39 | 40 | let provider = ProviderBuilder::new().connect_client(rpc_client); 41 | 42 | let num = provider.get_block_number().await.unwrap(); 43 | 44 | assert_eq!(num, 0); 45 | 46 | Ok(()) 47 | } 48 | 49 | // Layer that will be stacked on top of the Hyper client. 50 | struct RequestModifyingLayer; 51 | 52 | // Implement the `Layer` trait for the custom layer. 53 | impl Layer for RequestModifyingLayer { 54 | type Service = RequestModifyingService; 55 | 56 | fn layer(&self, inner: S) -> Self::Service { 57 | RequestModifyingService { inner } 58 | } 59 | } 60 | 61 | // Service that will modify the request before it is sent. 62 | #[derive(Clone)] // Service must be Cloneable. 63 | struct RequestModifyingService { 64 | inner: S, 65 | } 66 | 67 | impl Service> for RequestModifyingService 68 | where 69 | S: Service, Response = HyperResponse, Error = Error> 70 | + Clone 71 | + Send 72 | + Sync 73 | + 'static, 74 | S::Future: Send, 75 | S::Error: std::error::Error + Send + Sync + 'static, 76 | B: From> + Send + 'static + Clone + Sync + std::fmt::Debug, 77 | { 78 | type Error = Error; 79 | type Future = HyperResponseFut; 80 | type Response = HyperResponse; 81 | 82 | fn poll_ready( 83 | &mut self, 84 | cx: &mut std::task::Context<'_>, 85 | ) -> std::task::Poll> { 86 | self.inner.poll_ready(cx) 87 | } 88 | 89 | fn call(&mut self, mut req: hyper::Request) -> Self::Future { 90 | // Modify the request here. 91 | 92 | // Example: Add a custom header to the request. 93 | let header = req.headers_mut(); 94 | header.insert("x-alloy", "hyper".parse().unwrap()); 95 | 96 | println!("Request: {req:?}"); 97 | 98 | let fut = self.inner.call(req); 99 | 100 | Box::pin(fut) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /examples/layers/examples/logging_layer.rs: -------------------------------------------------------------------------------- 1 | //! This examples demonstrates how to implement your own custom transport layer. 2 | //! As a demonstration we implement a simple request / response logging layer. 3 | 4 | use std::{ 5 | fmt::Debug, 6 | future::{Future, IntoFuture}, 7 | pin::Pin, 8 | task::{Context, Poll}, 9 | }; 10 | 11 | use alloy::{ 12 | node_bindings::Anvil, 13 | providers::{Provider, ProviderBuilder}, 14 | rpc::{ 15 | client::ClientBuilder, 16 | json_rpc::{RequestPacket, ResponsePacket}, 17 | }, 18 | transports::TransportError, 19 | }; 20 | use eyre::Result; 21 | use tower::{Layer, Service}; 22 | 23 | struct LoggingLayer; 24 | 25 | // Implement tower::Layer for LoggingLayer. 26 | impl Layer for LoggingLayer { 27 | type Service = LoggingService; 28 | 29 | fn layer(&self, inner: S) -> Self::Service { 30 | LoggingService { inner } 31 | } 32 | } 33 | 34 | // A logging service that wraps an inner service. 35 | #[derive(Debug, Clone)] 36 | struct LoggingService { 37 | inner: S, 38 | } 39 | 40 | // Implement tower::Service for LoggingService. 41 | impl Service for LoggingService 42 | where 43 | // Constraints on the service. 44 | S: Service, 45 | S::Future: Send + 'static, 46 | S::Response: Send + 'static + Debug, 47 | S::Error: Send + 'static + Debug, 48 | { 49 | type Response = S::Response; 50 | type Error = S::Error; 51 | type Future = Pin> + Send>>; 52 | 53 | fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { 54 | self.inner.poll_ready(cx) 55 | } 56 | 57 | fn call(&mut self, req: RequestPacket) -> Self::Future { 58 | println!("Request: {req:?}"); 59 | 60 | let fut = self.inner.call(req); 61 | 62 | Box::pin(async move { 63 | let res = fut.await; 64 | println!("Response: {res:?}"); 65 | res 66 | }) 67 | } 68 | } 69 | 70 | #[tokio::main] 71 | async fn main() -> Result<()> { 72 | // Spin up a local Anvil node. 73 | // Ensure `anvil` is available in $PATH. 74 | let anvil = Anvil::new().spawn(); 75 | 76 | // Create a new client with the logging layer. 77 | let rpc_url = anvil.endpoint_url(); 78 | let client = ClientBuilder::default().layer(LoggingLayer).http(rpc_url); 79 | 80 | // Create a new provider with the client. 81 | let provider = ProviderBuilder::new().connect_client(client); 82 | 83 | for _ in 0..10 { 84 | let _block_number = provider.get_block_number().into_future().await?; 85 | } 86 | 87 | Ok(()) 88 | } 89 | -------------------------------------------------------------------------------- /examples/layers/examples/retry_layer.rs: -------------------------------------------------------------------------------- 1 | //! This example demonstrates how to use the [`RetryBackoffLayer`] in the provider. 2 | 3 | use alloy::{ 4 | node_bindings::Anvil, 5 | providers::{Provider, ProviderBuilder}, 6 | rpc::client::RpcClient, 7 | transports::layers::RetryBackoffLayer, 8 | }; 9 | 10 | #[tokio::main] 11 | async fn main() -> eyre::Result<()> { 12 | let anvil = Anvil::new().spawn(); 13 | 14 | // The maximum number of retries for rate limit errors 15 | let max_retry = 10; 16 | 17 | // The initial backoff in milliseconds 18 | let backoff = 1000; 19 | 20 | // The number of compute units per second for this provider 21 | let cups = 100; 22 | 23 | // Instantiate the RetryBackoffLayer with the configuration 24 | let retry_layer = RetryBackoffLayer::new(max_retry, backoff, cups); 25 | 26 | // Add the layer to the transport client. 27 | // The layer will retry all requests that return a rate limit error (eg. 429) until max_retries 28 | // have been reached. 29 | let client = RpcClient::builder().layer(retry_layer).http(anvil.endpoint_url()); 30 | 31 | let provider = ProviderBuilder::new().connect_client(client); 32 | 33 | let latest_block = provider.get_block_number().await?; 34 | 35 | assert_eq!(latest_block, 0); 36 | 37 | Ok(()) 38 | } 39 | -------------------------------------------------------------------------------- /examples/node-bindings/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples-node-bindings" 3 | publish.workspace = true 4 | version.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dev-dependencies] 16 | alloy.workspace = true 17 | 18 | eyre.workspace = true 19 | tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } 20 | -------------------------------------------------------------------------------- /examples/node-bindings/examples/anvil_deploy_contract.rs: -------------------------------------------------------------------------------- 1 | //! Example of deploying a contract to a local Anvil node using the [`ProviderBuilder`]. 2 | 3 | use alloy::{primitives::U256, providers::ProviderBuilder, sol}; 4 | use eyre::Result; 5 | 6 | // Codegen from embedded Solidity code and precompiled bytecode. 7 | sol! { 8 | #[allow(missing_docs)] 9 | // solc v0.8.26; solc Counter.sol --via-ir --optimize --bin 10 | #[sol(rpc, bytecode="6080806040523460135760df908160198239f35b600080fdfe6080806040526004361015601257600080fd5b60003560e01c9081633fb5c1cb1460925781638381f58a146079575063d09de08a14603c57600080fd5b3460745760003660031901126074576000546000198114605e57600101600055005b634e487b7160e01b600052601160045260246000fd5b600080fd5b3460745760003660031901126074576020906000548152f35b34607457602036600319011260745760043560005500fea2646970667358221220e978270883b7baed10810c4079c941512e93a7ba1cd1108c781d4bc738d9090564736f6c634300081a0033")] 11 | contract Counter { 12 | uint256 public number; 13 | 14 | function setNumber(uint256 newNumber) public { 15 | number = newNumber; 16 | } 17 | 18 | function increment() public { 19 | number++; 20 | } 21 | } 22 | } 23 | 24 | #[tokio::main] 25 | async fn main() -> Result<()> { 26 | // Spin up a local Anvil node. 27 | // Ensure `anvil` is available in $PATH. 28 | let provider = ProviderBuilder::new().connect_anvil_with_wallet(); 29 | 30 | // Deploy the `Counter` contract. 31 | let contract = Counter::deploy(&provider).await?; 32 | 33 | println!("Deployed contract at address: {}", contract.address()); 34 | 35 | // Set the number to 42. 36 | let builder = contract.setNumber(U256::from(42)); 37 | let tx_hash = builder.send().await?.watch().await?; 38 | 39 | println!("Set number to 42: {tx_hash}"); 40 | 41 | // Increment the number to 43. 42 | let builder = contract.increment(); 43 | let tx_hash = builder.send().await?.watch().await?; 44 | 45 | println!("Incremented number: {tx_hash}"); 46 | 47 | // Retrieve the number, which should be 43. 48 | let builder = contract.number(); 49 | let number = builder.call().await?; 50 | 51 | println!("Retrieved number: {number}"); 52 | 53 | Ok(()) 54 | } 55 | -------------------------------------------------------------------------------- /examples/node-bindings/examples/anvil_fork_instance.rs: -------------------------------------------------------------------------------- 1 | //! Example of spinning up a forked Anvil instance and connecting it with a provider. 2 | 3 | use alloy::{ 4 | node_bindings::Anvil, 5 | providers::{ext::AnvilApi, ProviderBuilder}, 6 | }; 7 | use eyre::Result; 8 | 9 | #[tokio::main] 10 | async fn main() -> Result<()> { 11 | // Spin up a forked Anvil node. 12 | // Ensure `anvil` is available in $PATH. 13 | let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc"; 14 | let anvil = Anvil::new().fork(rpc_url).try_spawn()?; 15 | let provider = ProviderBuilder::new().connect_http(anvil.endpoint_url()); 16 | 17 | // Get node info using the Anvil API. 18 | let info = provider.anvil_node_info().await?; 19 | 20 | println!("Node info: {info:#?}"); 21 | 22 | assert_eq!(info.environment.chain_id, 1); 23 | assert_eq!(info.fork_config.fork_url, Some(rpc_url.to_string())); 24 | 25 | Ok(()) 26 | } 27 | -------------------------------------------------------------------------------- /examples/node-bindings/examples/anvil_fork_provider.rs: -------------------------------------------------------------------------------- 1 | //! Example of spinning up a forked Anvil node using the [`ProviderBuilder`]. 2 | 3 | use alloy::providers::{ext::AnvilApi, ProviderBuilder}; 4 | use eyre::Result; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<()> { 8 | // Spin up a forked Anvil node. 9 | // Ensure `anvil` is available in $PATH. 10 | let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc"; 11 | let provider = ProviderBuilder::new().connect_anvil_with_config(|anvil| anvil.fork(rpc_url)); 12 | 13 | // Get node info using the Anvil API. 14 | let info = provider.anvil_node_info().await?; 15 | 16 | println!("Node info: {info:#?}"); 17 | 18 | assert_eq!(info.environment.chain_id, 1); 19 | assert_eq!(info.fork_config.fork_url, Some(rpc_url.to_string())); 20 | 21 | Ok(()) 22 | } 23 | -------------------------------------------------------------------------------- /examples/node-bindings/examples/anvil_local_instance.rs: -------------------------------------------------------------------------------- 1 | //! Example of spinning up a local Anvil instance and connecting it with a provider. 2 | 3 | use alloy::{ 4 | node_bindings::Anvil, 5 | providers::{ext::AnvilApi, ProviderBuilder}, 6 | }; 7 | use eyre::Result; 8 | 9 | #[tokio::main] 10 | async fn main() -> Result<()> { 11 | // Spin up a local Anvil node. 12 | // Ensure `anvil` is available in $PATH. 13 | let anvil = Anvil::new().block_time(1).chain_id(1337).try_spawn()?; 14 | let provider = ProviderBuilder::new().connect_http(anvil.endpoint_url()); 15 | 16 | // Get node info using the Anvil API. 17 | let info = provider.anvil_node_info().await?; 18 | 19 | println!("Node info: {info:#?}"); 20 | 21 | assert_eq!(info.environment.chain_id, 1337); 22 | assert_eq!(info.fork_config.fork_url, None); 23 | 24 | Ok(()) 25 | } 26 | -------------------------------------------------------------------------------- /examples/node-bindings/examples/anvil_local_provider.rs: -------------------------------------------------------------------------------- 1 | //! Example of spinning up a local Anvil node using the [`ProviderBuilder`]. 2 | 3 | use alloy::providers::{ext::AnvilApi, ProviderBuilder}; 4 | use eyre::Result; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<()> { 8 | // Spin up a local Anvil node. 9 | // Ensure `anvil` is available in $PATH. 10 | let provider = ProviderBuilder::new() 11 | .connect_anvil_with_config(|anvil| anvil.block_time(1).chain_id(1337)); 12 | 13 | // Get node info using the Anvil API. 14 | let info = provider.anvil_node_info().await?; 15 | 16 | println!("Node info: {info:#?}"); 17 | 18 | assert_eq!(info.environment.chain_id, 1337); 19 | assert_eq!(info.fork_config.fork_url, None); 20 | 21 | Ok(()) 22 | } 23 | -------------------------------------------------------------------------------- /examples/node-bindings/examples/anvil_set_storage_at.rs: -------------------------------------------------------------------------------- 1 | //! Example of mocking WETH balance of a target account using [`AnvilApi::anvil_set_storage_at`]. 2 | 3 | use alloy::{ 4 | primitives::{address, keccak256, utils::parse_units, Address, U256}, 5 | providers::{ext::AnvilApi, ProviderBuilder}, 6 | sol, 7 | sol_types::SolValue, 8 | }; 9 | use eyre::Result; 10 | 11 | sol!( 12 | #[allow(missing_docs)] 13 | #[sol(rpc)] 14 | contract IERC20 { 15 | function balanceOf(address target) returns (uint256); 16 | } 17 | ); 18 | 19 | static WETH_ADDR: Address = address!("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"); 20 | 21 | #[tokio::main] 22 | async fn main() -> Result<()> { 23 | // Spin up a forked Anvil node. 24 | // Ensure `anvil` is available in $PATH. 25 | let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc"; 26 | let provider = ProviderBuilder::new().connect_anvil_with_config(|anvil| anvil.fork(rpc_url)); 27 | 28 | // Create an instance of the WETH contract. 29 | let iweth = IERC20::new(WETH_ADDR, provider.clone()); 30 | 31 | // Random empty account. 32 | let account = address!("F605F9d1cB055E87E30bcAEe4CB9389a35aBe8Ff"); 33 | 34 | // Get the WETH balance of the target account before mocking. 35 | let balance_before = iweth.balanceOf(account).call().await?; 36 | println!("WETH balance before: {balance_before}"); 37 | assert_eq!(balance_before, U256::ZERO); 38 | 39 | // Mock WETH balance using the Anvil API. 40 | let hashed_slot = keccak256((account, U256::from(3)).abi_encode()); 41 | let mocked_balance: U256 = parse_units("1.0", "ether")?.into(); 42 | provider.anvil_set_storage_at(WETH_ADDR, hashed_slot.into(), mocked_balance.into()).await?; 43 | 44 | // Get the WETH balance of the target account after mocking. 45 | let balance_after = iweth.balanceOf(account).call().await?; 46 | println!("WETH balance after: {balance_after}"); 47 | assert_eq!(balance_after, mocked_balance); 48 | 49 | Ok(()) 50 | } 51 | -------------------------------------------------------------------------------- /examples/node-bindings/examples/geth_local_instance.rs: -------------------------------------------------------------------------------- 1 | //! Example of spinning up a local Geth node instance and connecting it with a provider. 2 | 3 | use alloy::{ 4 | node_bindings::Geth, 5 | providers::{Provider, ProviderBuilder}, 6 | }; 7 | use eyre::Result; 8 | 9 | #[tokio::main] 10 | async fn main() -> Result<()> { 11 | // Spin up a local Geth node. 12 | // Ensure `geth` is available in $PATH. 13 | let geth = Geth::new().chain_id(1337).port(8545_u16).authrpc_port(8551).spawn(); 14 | let provider = ProviderBuilder::new().connect_http(geth.endpoint().parse()?); 15 | 16 | let chain_id = provider.get_chain_id().await?; 17 | 18 | println!("Geth running at: {} with chain id: {chain_id}", geth.endpoint()); 19 | 20 | assert_eq!(chain_id, 1337); 21 | assert_eq!(geth.port(), 8545); 22 | assert_eq!(geth.auth_port(), Some(8551)); 23 | assert_eq!(geth.p2p_port(), None); 24 | 25 | Ok(()) 26 | } 27 | -------------------------------------------------------------------------------- /examples/node-bindings/examples/reth_local_instance.rs: -------------------------------------------------------------------------------- 1 | //! Example of spinning up a local Reth node instance and connecting it with a provider. 2 | 3 | use alloy::{ 4 | node_bindings::Reth, 5 | providers::{Provider, ProviderBuilder}, 6 | }; 7 | use eyre::Result; 8 | 9 | #[tokio::main] 10 | async fn main() -> Result<()> { 11 | // Spin up a local Reth node. 12 | // Ensure `reth` is available in $PATH. 13 | let reth = Reth::new().dev().disable_discovery().instance(1).spawn(); 14 | let provider = ProviderBuilder::new().connect_http(reth.endpoint().parse()?); 15 | 16 | let chain_id = provider.get_chain_id().await?; 17 | 18 | println!("Reth running at: {} with chain id: {chain_id}", reth.endpoint()); 19 | 20 | assert_eq!(chain_id, 1337); 21 | assert_eq!(reth.http_port(), 8545); 22 | assert_eq!(reth.ws_port(), 8546); 23 | assert_eq!(reth.auth_port(), Some(8551)); 24 | assert_eq!(reth.p2p_port(), None); 25 | 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /examples/primitives/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples-primitives" 3 | publish.workspace = true 4 | version.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dev-dependencies] 16 | alloy.workspace = true 17 | 18 | eyre.workspace = true 19 | -------------------------------------------------------------------------------- /examples/primitives/examples/bytes_and_address_types.rs: -------------------------------------------------------------------------------- 1 | //! Example of basic usage of bytes and address types and macros. 2 | 3 | use alloy::primitives::{ 4 | address, b128, b256, b512, b64, bytes, fixed_bytes, Address, Bytes, FixedBytes, 5 | }; 6 | use eyre::Result; 7 | 8 | fn main() -> Result<()> { 9 | // Bytes type 10 | let a = bytes!("0123abcd"); 11 | assert_eq!(a, Bytes::from(&[0x01, 0x23, 0xab, 0xcd])); 12 | assert_eq!(a.len(), 4); 13 | 14 | // Address type 15 | let b = address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266"); 16 | assert_eq!( 17 | b, 18 | Address::from(&[ 19 | 0xf3, 0x9f, 0xd6, 0xe5, 0x1a, 0xad, 0x88, 0xf6, 0xf4, 0xce, 0x6a, 0xb8, 0x82, 0x72, 20 | 0x79, 0xcf, 0xff, 0xb9, 0x22, 0x66 21 | ]) 22 | ); 23 | assert_eq!(b.len(), 20); 24 | 25 | // FixedBytes<8> type 26 | let c = b64!("0102030405060708"); 27 | assert_eq!(c, FixedBytes::from(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08])); 28 | assert_eq!(c.len(), 8); 29 | 30 | // FixedBytes<16> type 31 | let d = b128!("0102030405060708090a0b0c0d0e0f10"); 32 | assert_eq!( 33 | d, 34 | FixedBytes::from(&[ 35 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 36 | 0x0f, 0x10, 37 | ]) 38 | ); 39 | assert_eq!(d.len(), 16); 40 | 41 | // FixedBytes<32> type 42 | let e = b256!("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"); 43 | assert_eq!( 44 | e, 45 | FixedBytes::from(&[ 46 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 47 | 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 48 | 0x1d, 0x1e, 0x1f, 0x20, 49 | ]), 50 | ); 51 | assert_eq!(e.len(), 32); 52 | 53 | // FixedBytes<64> type 54 | let f = b512!("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40"); 55 | assert_eq!( 56 | f, 57 | FixedBytes::from(&[ 58 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 59 | 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 60 | 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 61 | 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 62 | 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 63 | ]), 64 | ); 65 | assert_eq!(f.len(), 64); 66 | 67 | // FixedBytes<20> type, determined by the length of the input 68 | let g = fixed_bytes!("0102030405060708090a0b0c0d0e0f1011121314"); 69 | assert_eq!( 70 | g, 71 | FixedBytes::from(&[ 72 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 73 | 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 74 | ]), 75 | ); 76 | assert_eq!(g.len(), 20); 77 | 78 | Ok(()) 79 | } 80 | -------------------------------------------------------------------------------- /examples/primitives/examples/hashing_functions.rs: -------------------------------------------------------------------------------- 1 | //! Example of basic usage of hashing functions. 2 | 3 | use alloy::primitives::{eip191_hash_message, keccak256}; 4 | use eyre::Result; 5 | 6 | fn main() -> Result<()> { 7 | // [`Keccak-256`]: https://en.wikipedia.org/wiki/SHA-3 8 | let hash = keccak256(b"hello world"); 9 | assert_eq!( 10 | hash.to_string(), 11 | "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad" 12 | ); 13 | assert_eq!(hash.len(), 32); 14 | 15 | // Hash a message according to [EIP-191] (version `0x01`). 16 | // 17 | // The final message is a UTF-8 string, encoded as follows: 18 | // `"\x19Ethereum Signed Message:\n" + message.length + message` 19 | // 20 | // This message is then hashed using [`Keccak-256`]: https://en.wikipedia.org/wiki/SHA-3. 21 | // 22 | // [EIP-191]: https://eips.ethereum.org/EIPS/eip-191 23 | let eip191_hash = eip191_hash_message(b"hello_world"); 24 | assert_eq!( 25 | eip191_hash.to_string(), 26 | "0xd52de6e039c023a7c77752126e4d9d99e2a7dacea3d19e97e9c2ebcb3ecf1c00" 27 | ); 28 | assert_eq!(eip191_hash.len(), 32); 29 | 30 | Ok(()) 31 | } 32 | -------------------------------------------------------------------------------- /examples/providers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples-providers" 3 | publish.workspace = true 4 | version.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dev-dependencies] 16 | alloy.workspace = true 17 | 18 | eyre.workspace = true 19 | futures-util.workspace = true 20 | tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } 21 | -------------------------------------------------------------------------------- /examples/providers/examples/basic_provider.rs: -------------------------------------------------------------------------------- 1 | //! Instantiate a basic provider without any fillers or layers. 2 | 3 | use eyre::Result; 4 | 5 | use alloy::{ 6 | network::TransactionBuilder, 7 | node_bindings::Anvil, 8 | primitives::{Address, U256}, 9 | providers::{Provider, ProviderBuilder}, 10 | rpc::types::TransactionRequest, 11 | signers::local::PrivateKeySigner, 12 | }; 13 | 14 | #[tokio::main] 15 | async fn main() -> Result<()> { 16 | // Spawn an Anvil instance 17 | // Make sure `anvil` is in $PATH 18 | let anvil = Anvil::new().try_spawn()?; 19 | let signer: PrivateKeySigner = anvil.keys()[0].clone().into(); 20 | let alice = signer.address(); 21 | 22 | let provider = ProviderBuilder::new() 23 | // Disable the recommended fillers that are enabled by default 24 | .disable_recommended_fillers() 25 | // Add the signer to the provider for signing transactions 26 | .wallet(signer) 27 | .connect_http(anvil.endpoint().parse()?); 28 | 29 | let bob = Address::from([0x42; 20]); 30 | let fees = provider.estimate_eip1559_fees().await?; 31 | let nonce = provider.get_transaction_count(alice).await?; 32 | let chain_id = provider.get_chain_id().await?; 33 | 34 | let tx = TransactionRequest::default() 35 | .with_value(U256::from(1)) 36 | .with_chain_id(chain_id) 37 | .with_from(alice) 38 | .with_nonce(nonce) 39 | .with_max_fee_per_gas(fees.max_fee_per_gas) 40 | .with_max_priority_fee_per_gas(fees.max_priority_fee_per_gas) 41 | .with_gas_limit(21000) 42 | .with_to(bob) 43 | .with_value(U256::from(1)); 44 | 45 | let bob_balance_before = provider.get_balance(bob).await?; 46 | let receipt = provider.send_transaction(tx).await?.get_receipt().await?; 47 | assert!(receipt.status(), "Transaction failed"); 48 | let bob_balance_after = provider.get_balance(bob).await?; 49 | println!("Balance before: {bob_balance_before}\nBalance after: {bob_balance_after}"); 50 | 51 | Ok(()) 52 | } 53 | -------------------------------------------------------------------------------- /examples/providers/examples/batch_rpc.rs: -------------------------------------------------------------------------------- 1 | //! Example depicting how to make a Batch RPC request using the HTTP provider. 2 | 3 | use alloy::{ 4 | node_bindings::Anvil, 5 | primitives::{address, U128, U64}, 6 | rpc::client::ClientBuilder, 7 | }; 8 | use eyre::Result; 9 | 10 | #[tokio::main] 11 | async fn main() -> Result<()> { 12 | // Spin up a local Anvil node. 13 | // Ensure `anvil` is available in $PATH. 14 | let anvil = Anvil::new().spawn(); 15 | 16 | // Swap this out with a RPC_URL provider that supports JSON-RPC batch requests. e.g. https://reth-ethereum.ithaca.xyz/rpc 17 | let rpc_url = anvil.endpoint_url(); 18 | 19 | // Create a HTTP transport. 20 | let client = ClientBuilder::default().http(rpc_url); 21 | 22 | // Instantiate a batch. 23 | let mut batch = client.new_batch(); 24 | 25 | // Add calls to the batch. 26 | let block_number_fut = 27 | batch.add_call("eth_blockNumber", &())?.map_resp(|resp: U64| resp.to::()); 28 | 29 | let gas_price_fut = 30 | batch.add_call("eth_gasPrice", &())?.map_resp(|resp: U128| resp.to::()); 31 | 32 | let vitalik = address!("d8da6bf26964af9d7eed9e03e53415d37aa96045"); 33 | let vitalik_nonce_fut = batch 34 | .add_call("eth_getTransactionCount", &(vitalik, "latest"))? // Vitalik's nonce at BlockId::Latest 35 | .map_resp(|resp: U128| resp.to::()); 36 | 37 | // Send the batch request. 38 | batch.send().await?; 39 | 40 | // Get the results. 41 | let (latest_block, gas_price, vitalik_nonce) = 42 | tokio::try_join!(block_number_fut, gas_price_fut, vitalik_nonce_fut)?; 43 | 44 | println!("Latest block number: {latest_block}"); 45 | println!("Gas price: {gas_price}"); 46 | println!("Vitalik's nonce: {vitalik_nonce}"); 47 | 48 | Ok(()) 49 | } 50 | -------------------------------------------------------------------------------- /examples/providers/examples/builder.rs: -------------------------------------------------------------------------------- 1 | //! Example of using the `ProviderBuilder` to create a provider with a signer and network. 2 | 3 | use alloy::{ 4 | network::TransactionBuilder, 5 | node_bindings::Anvil, 6 | primitives::U256, 7 | providers::{Provider, ProviderBuilder}, 8 | rpc::types::TransactionRequest, 9 | signers::local::PrivateKeySigner, 10 | }; 11 | use eyre::Result; 12 | 13 | #[tokio::main] 14 | async fn main() -> Result<()> { 15 | // Spin up a local Anvil node. 16 | // Ensure `anvil` is available in $PATH. 17 | let anvil = Anvil::new().block_time(1).try_spawn()?; 18 | 19 | // Set up signer from the first default Anvil account (Alice). 20 | let signer: PrivateKeySigner = anvil.keys()[0].clone().into(); 21 | 22 | // Create two users, Alice and Bob. 23 | let alice = signer.address(); 24 | let bob = anvil.addresses()[1]; 25 | 26 | // Set up the HTTP provider with the `reqwest` crate. 27 | let rpc_url = anvil.endpoint_url(); 28 | let provider = ProviderBuilder::new().wallet(signer).connect_http(rpc_url); 29 | 30 | // Create a transaction. 31 | let tx = TransactionRequest::default().with_to(bob).with_value(U256::from(100)); 32 | 33 | // Send the transaction and wait for the broadcast. 34 | let pending_tx = provider.send_transaction(tx).await?; 35 | 36 | println!("Pending transaction... {}", pending_tx.tx_hash()); 37 | 38 | // Wait for the transaction to be included and get the receipt. 39 | let receipt = pending_tx.get_receipt().await?; 40 | 41 | println!( 42 | "Transaction included in block {}", 43 | receipt.block_number.expect("Failed to get block number") 44 | ); 45 | 46 | assert_eq!(receipt.from, alice); 47 | assert_eq!(receipt.to, Some(bob)); 48 | 49 | Ok(()) 50 | } 51 | -------------------------------------------------------------------------------- /examples/providers/examples/builtin.rs: -------------------------------------------------------------------------------- 1 | //! Example of using the `on_builtin` method in the provider. 2 | 3 | use alloy::{ 4 | node_bindings::Anvil, 5 | providers::{Provider, ProviderBuilder}, 6 | }; 7 | use eyre::Result; 8 | use futures_util::StreamExt; 9 | 10 | #[tokio::main] 11 | async fn main() -> Result<()> { 12 | // Spin up a local Anvil node. 13 | // Ensure `anvil` is available in $PATH. 14 | let anvil = Anvil::new().block_time(1).try_spawn()?; 15 | 16 | // Instantiate a HTTP transport provider by passing the HTTP endpoint url. 17 | let http_rpc_url = anvil.endpoint(); 18 | let http_provider = ProviderBuilder::new().connect(&http_rpc_url).await?; 19 | 20 | // Get latest block number. 21 | let block_number = http_provider.get_block_number().await?; 22 | 23 | println!("Latest block number: {block_number:?}"); 24 | 25 | // This requires the `pubsub` and `ws` features to be enabled. 26 | let ws_rpc_url = anvil.ws_endpoint(); 27 | let ws_provider = ProviderBuilder::new().connect(&ws_rpc_url).await?; 28 | 29 | let sub = ws_provider.subscribe_blocks().await?; 30 | 31 | let mut stream = sub.into_stream().take(2); 32 | 33 | println!("Awaiting block headers..."); 34 | 35 | let handle = tokio::spawn(async move { 36 | while let Some(header) = stream.next().await { 37 | println!("{}", header.number); 38 | } 39 | }); 40 | 41 | handle.await?; 42 | 43 | // This requires the `pubsub` and `ipc` features to be enabled. 44 | // This would throw a runtime error if the ipc does not exist. 45 | let ipc_path = "/tmp/reth.ipc"; 46 | let ipc_provider = ProviderBuilder::new().connect(ipc_path).await?; 47 | 48 | let _block_number = ipc_provider.get_block_number().await?; 49 | 50 | Ok(()) 51 | } 52 | -------------------------------------------------------------------------------- /examples/providers/examples/dyn_provider.rs: -------------------------------------------------------------------------------- 1 | //! Demonstrates how to obtain a `DynProvider` from a Provider. 2 | 3 | use alloy::{ 4 | node_bindings::Anvil, 5 | providers::{Provider, ProviderBuilder}, 6 | signers::local::PrivateKeySigner, 7 | sol, 8 | }; 9 | 10 | // Codegen from embedded Solidity code and precompiled bytecode. 11 | sol! { 12 | #[allow(missing_docs)] 13 | // solc v0.8.26; solc Counter.sol --via-ir --optimize --bin 14 | #[sol(rpc, bytecode="6080806040523460135760df908160198239f35b600080fdfe6080806040526004361015601257600080fd5b60003560e01c9081633fb5c1cb1460925781638381f58a146079575063d09de08a14603c57600080fd5b3460745760003660031901126074576000546000198114605e57600101600055005b634e487b7160e01b600052601160045260246000fd5b600080fd5b3460745760003660031901126074576020906000548152f35b34607457602036600319011260745760043560005500fea2646970667358221220e978270883b7baed10810c4079c941512e93a7ba1cd1108c781d4bc738d9090564736f6c634300081a0033")] 15 | contract Counter { 16 | uint256 public number; 17 | 18 | function setNumber(uint256 newNumber) public { 19 | number = newNumber; 20 | } 21 | 22 | function increment() public { 23 | number++; 24 | } 25 | } 26 | } 27 | 28 | #[tokio::main] 29 | async fn main() -> eyre::Result<()> { 30 | // Spin up a local Anvil node. 31 | // Ensure `anvil` is available in $PATH. 32 | let anvil = Anvil::new().spawn(); 33 | 34 | let signer_pk: PrivateKeySigner = 35 | "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse()?; 36 | 37 | let from = signer_pk.address(); 38 | 39 | // Provider with verbose types. 40 | let regular_provider = 41 | ProviderBuilder::new().wallet(signer_pk).connect(anvil.endpoint().as_str()).await?; 42 | 43 | // One can use the erased method to obtain a DynProvider from a Provider. 44 | let dyn_provider = regular_provider.erased(); 45 | 46 | // Note that the fillers set while building provider are still available, only the types have 47 | // been erased OR boxed under the hood. 48 | // This enables us to use the DynProvider as one would use a regular Provider with verbose 49 | // types. 50 | let counter = Counter::deploy(&dyn_provider).await?; 51 | 52 | println!("Counter deployed at {}", counter.address()); 53 | 54 | // Sends a transaction with required properties such as gas, nonce, from filled. 55 | let incr = counter.increment().send().await?; 56 | let receipt = incr.get_receipt().await?; 57 | assert_eq!(receipt.from, from); 58 | 59 | let number = counter.number().call().await?; 60 | 61 | println!("New number: {number}"); 62 | 63 | Ok(()) 64 | } 65 | -------------------------------------------------------------------------------- /examples/providers/examples/http.rs: -------------------------------------------------------------------------------- 1 | //! Example of using the HTTP provider with the `reqwest` crate to get the latest block number. 2 | 3 | use alloy::providers::{Provider, ProviderBuilder}; 4 | use eyre::Result; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<()> { 8 | // Create a provider with the HTTP transport using the `reqwest` crate. 9 | let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc".parse()?; 10 | let provider = ProviderBuilder::new().connect_http(rpc_url); 11 | 12 | // Get latest block number. 13 | let latest_block = provider.get_block_number().await?; 14 | 15 | println!("Latest block number: {latest_block}"); 16 | 17 | Ok(()) 18 | } 19 | -------------------------------------------------------------------------------- /examples/providers/examples/http_with_auth.rs: -------------------------------------------------------------------------------- 1 | //! Example of using the reqwest HTTP client with an `Authorization` header to get the latest block 2 | //! number. 3 | 4 | use alloy::{ 5 | providers::{Provider, ProviderBuilder}, 6 | rpc::client::RpcClient, 7 | transports::http::{ 8 | reqwest::{ 9 | header::{HeaderMap, HeaderValue, AUTHORIZATION}, 10 | Client, 11 | }, 12 | Http, 13 | }, 14 | }; 15 | use eyre::Result; 16 | 17 | #[tokio::main] 18 | async fn main() -> Result<()> { 19 | // Set the Authorization header. 20 | let mut headers = HeaderMap::new(); 21 | headers.insert(AUTHORIZATION, HeaderValue::from_static("deadbeef")); 22 | 23 | // Create the reqwest::Client with the AUTHORIZATION header. 24 | let client_with_auth = Client::builder().default_headers(headers).build()?; 25 | 26 | // Create the HTTP transport. 27 | let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc".parse()?; 28 | let http = Http::with_client(client_with_auth, rpc_url); 29 | let rpc_client = RpcClient::new(http, false); 30 | 31 | // Create a provider with the HTTP transport. 32 | let provider = ProviderBuilder::new().connect_client(rpc_client); 33 | 34 | // Get latest block number. 35 | let latest_block = provider.get_block_number().await?; 36 | 37 | println!("Latest block number: {latest_block}"); 38 | 39 | Ok(()) 40 | } 41 | -------------------------------------------------------------------------------- /examples/providers/examples/ipc.rs: -------------------------------------------------------------------------------- 1 | //! Example of using the IPC provider to get the latest block number. 2 | 3 | use alloy::providers::{IpcConnect, Provider, ProviderBuilder}; 4 | use eyre::Result; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<()> { 8 | // Set up the IPC transport which is consumed by the RPC client. 9 | let ipc_path = "/tmp/reth.ipc"; 10 | 11 | // Create the provider. 12 | let ipc = IpcConnect::new(ipc_path.to_string()); 13 | let provider = ProviderBuilder::new().connect_ipc(ipc).await?; 14 | 15 | let latest_block = provider.get_block_number().await?; 16 | 17 | println!("Latest block: {latest_block}"); 18 | 19 | Ok(()) 20 | } 21 | -------------------------------------------------------------------------------- /examples/providers/examples/mocking.rs: -------------------------------------------------------------------------------- 1 | //! This example shows to how to use the `MockTransport` to mock the provider responses for testing 2 | //! purposes. 3 | //! 4 | //! This aids in testing parts of your code that relies on provider without having to connect to a 5 | //! network. 6 | use alloy::{ 7 | primitives::{Address, U256}, 8 | providers::{Provider, ProviderBuilder}, 9 | rpc::json_rpc::ErrorPayload, 10 | transports::mock::Asserter, 11 | }; 12 | 13 | #[tokio::main] 14 | async fn main() -> eyre::Result<()> { 15 | // Asserter is used to push responses to the provided whether success or failure as you'll see 16 | // in the next steps. Note that the `Asserter` wraps a FIFO queue and responses are returned 17 | // in the order they are pushed. 18 | let asserter = Asserter::new(); 19 | 20 | // Initialize the provider with the `MockTransport` that intercepts incoming requests and uses 21 | // the `Asserter` to return the next response. 22 | // `Asserter` is cheaply cloneable as the underlying queue is wrapped in an `Arc`. 23 | let provider = ProviderBuilder::new().connect_mocked_client(asserter.clone()); 24 | 25 | // Mock the response for a basic `get_block_number` request. 26 | let expected_bn = 1000; 27 | // The `.push_success` accepts any type that implements `serde::Serialize` 28 | asserter.push_success(&expected_bn); 29 | // Push an error response. 30 | asserter.push_failure_msg("SOME ERROR"); 31 | // One can also push a custom `ErrorPayload` response that you're expecting the RPC server to 32 | // return. 33 | asserter.push_failure(ErrorPayload::invalid_request()); 34 | 35 | let actual_bn = provider.get_block_number().await?; 36 | assert_eq!(actual_bn, expected_bn); 37 | 38 | // Mock an error response. 39 | let err = provider.get_block_number().await.unwrap_err(); 40 | assert!(err.as_error_resp().unwrap().to_string().contains("SOME ERROR")); 41 | 42 | // Mock a custom error response. 43 | let err = provider.get_block_number().await.unwrap_err(); 44 | assert_eq!(err.as_error_resp().unwrap().code, -32600); 45 | assert_eq!(err.as_error_resp().unwrap().message, "Invalid Request"); 46 | 47 | // Mocking a certain response and expecting a different response will lead to a deserialization 48 | // error. 49 | let expected_balance = U256::MAX; 50 | asserter.push_success(&expected_balance); 51 | 52 | // Try to get block_number instead of balance. 53 | // This will fail because the next response is going to be U256::MAX, but `get_block_number` 54 | // tries to deserialize the response to `u64` which is not possible. 55 | let err = provider.get_block_number().await.unwrap_err(); 56 | assert!(err.is_deser_error()); 57 | 58 | // Since we're using the `MockTransport`, we can assert raw JSON-RPC requests as well. 59 | let expected_balance = U256::from(1000); 60 | asserter.push_success(&expected_balance); 61 | 62 | // Raw request 63 | let addr = Address::with_last_byte(1); 64 | let balance: U256 = provider.raw_request("eth_getBalance".into(), (addr,)).await?; 65 | 66 | assert_eq!(balance, expected_balance); 67 | 68 | Ok(()) 69 | } 70 | -------------------------------------------------------------------------------- /examples/providers/examples/multicall_batching.rs: -------------------------------------------------------------------------------- 1 | //! Demonstrate the Multicall Batch Layer. 2 | //! Provider layer that aggregates contract calls (`eth_call`) over a time period into a single 3 | //! Multicall3 contract call. This is useful for reducing the number of requests made to the RPC. 4 | 5 | use std::time::Duration; 6 | 7 | use alloy::{ 8 | primitives::address, 9 | providers::{layers::CallBatchLayer, Provider, ProviderBuilder}, 10 | sol, 11 | }; 12 | use eyre::Result; 13 | use IWETH9::{balanceOfCall, totalSupplyCall, IWETH9Instance}; 14 | 15 | sol!( 16 | #[allow(missing_docs)] 17 | #[sol(rpc)] 18 | #[derive(Debug)] 19 | IWETH9, 20 | "examples/abi/IWETH9.json" 21 | ); 22 | 23 | #[tokio::main] 24 | async fn main() -> Result<()> { 25 | // Instantiate a provider with the `CallBatchLayer` enabled. 26 | let provider = ProviderBuilder::new() 27 | // Enables `eth_call` batching by leveraging the Multicall3 contract. 28 | // The `CallBatchLayer` will wait for a certain amount of time before sending a request. See: . 29 | // This delay is added to aggregate any incoming `eth_calls` that can be together. 30 | // In this case, we set the delay to 10ms. 31 | .layer(CallBatchLayer::new().wait(Duration::from_secs(10))) 32 | // Can also use the shorthand `with_call_batching` on the build which set the delay to 1ms. 33 | // .with_call_batching() 34 | .connect_anvil_with_wallet_and_config(|a| a.fork("https://reth-ethereum.ithaca.xyz/rpc"))?; 35 | 36 | // Create a new instance of the IWETH9 contract. 37 | let weth = 38 | IWETH9Instance::new(address!("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"), &provider); 39 | 40 | let alice = address!("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"); 41 | let bob = address!("0xc7bBeC68d12a0d1830360F8Ec58fA599bA1b0e9b"); 42 | 43 | // Calls that will be batched. 44 | let alice_weth = weth.balanceOf(alice).into_transaction_request(); 45 | let bob_weth = weth.balanceOf(bob).into_transaction_request(); 46 | let total_supply = weth.totalSupply().into_transaction_request(); 47 | 48 | // Requests need to be parallelized to be batched. 49 | let (alice_weth, bob_weth, total_supply, block_number, alice_eth) = tokio::try_join!( 50 | // Batch `eth_call` requests. 51 | provider.call(alice_weth).decode_resp::(), 52 | provider.call(bob_weth).decode_resp::(), 53 | provider.call(total_supply).decode_resp::(), 54 | // Get block number and get balance calls can also be batched. 55 | provider.get_block_number(), 56 | provider.get_balance(alice) 57 | )?; 58 | 59 | // Resolve `Ok`. 60 | let alice_weth = alice_weth?; 61 | let bob_weth = bob_weth?; 62 | let total_supply = total_supply?; 63 | 64 | println!("Block Number: {block_number}"); 65 | println!( 66 | "Alice's WETH balance: {alice_weth}\nBob's WETH balance: {bob_weth}\nTotal WETH supply: {total_supply}\nAlice's ETH 67 | balance: {alice_eth}" 68 | ); 69 | 70 | Ok(()) 71 | } 72 | -------------------------------------------------------------------------------- /examples/providers/examples/ws.rs: -------------------------------------------------------------------------------- 1 | //! Example of using the WS provider to subscribe to new blocks. 2 | 3 | use alloy::providers::{Provider, ProviderBuilder, WsConnect}; 4 | use eyre::Result; 5 | use futures_util::StreamExt; 6 | 7 | #[tokio::main] 8 | async fn main() -> Result<()> { 9 | // Create the provider. 10 | let rpc_url = "wss://eth-mainnet.g.alchemy.com/v2/your-api-key"; 11 | let ws = WsConnect::new(rpc_url); 12 | let provider = ProviderBuilder::new().connect_ws(ws).await?; 13 | 14 | // Subscribe to new blocks. 15 | let sub = provider.subscribe_blocks().await?; 16 | 17 | // Wait and take the next 4 blocks. 18 | let mut stream = sub.into_stream().take(4); 19 | 20 | println!("Awaiting block headers..."); 21 | 22 | // Take the stream and print the block number upon receiving a new block. 23 | let handle = tokio::spawn(async move { 24 | while let Some(header) = stream.next().await { 25 | println!("Latest block number: {}", header.number); 26 | } 27 | }); 28 | 29 | handle.await?; 30 | 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /examples/providers/examples/ws_with_auth.rs: -------------------------------------------------------------------------------- 1 | //! Example of using the WS provider with auth to subscribe to new blocks. 2 | 3 | use alloy::{ 4 | providers::{Provider, ProviderBuilder, WsConnect}, 5 | transports::Authorization, 6 | }; 7 | use eyre::Result; 8 | use futures_util::StreamExt; 9 | 10 | #[tokio::main] 11 | async fn main() -> Result<()> { 12 | // Create authorization methods. 13 | let auth = Authorization::basic("username", "password"); 14 | let auth_bearer = Authorization::bearer("bearer-token"); 15 | 16 | // Create the WS connection object with authentication. 17 | let rpc_url = "wss://your-ws-endpoint.com/"; 18 | let ws_basic = WsConnect::new(rpc_url).with_auth(auth); 19 | let ws_bearer = WsConnect::new(rpc_url).with_auth(auth_bearer); 20 | 21 | // Create the provider. 22 | let provider_basic = ProviderBuilder::new().connect_ws(ws_basic).await?; 23 | let provider_bearer = ProviderBuilder::new().connect_ws(ws_bearer).await?; 24 | 25 | // Subscribe to new block headers. 26 | let sub_basic = provider_basic.subscribe_blocks(); 27 | let sub_bearer = provider_bearer.subscribe_blocks(); 28 | 29 | // Wait and take the next 4 block headers 30 | let mut stream_basic = sub_basic.await?.into_stream().take(4); 31 | let mut stream_bearer = sub_bearer.await?.into_stream().take(4); 32 | 33 | println!("Awaiting block headers..."); 34 | 35 | // Take the basic stream and print the block number upon receiving a new block header. 36 | let basic_handle = tokio::spawn(async move { 37 | while let Some(header) = stream_basic.next().await { 38 | println!("Latest block number (basic): {}", header.number); 39 | } 40 | }); 41 | 42 | // Take the bearer stream and print the block number upon receiving a new block header. 43 | let bearer_handle = tokio::spawn(async move { 44 | while let Some(header) = stream_bearer.next().await { 45 | println!("Latest block number (bearer): {}", header.number); 46 | } 47 | }); 48 | 49 | // Wait for both tasks to complete. 50 | let _ = tokio::try_join!(basic_handle, bearer_handle)?; 51 | 52 | Ok(()) 53 | } 54 | -------------------------------------------------------------------------------- /examples/queries/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples-queries" 3 | publish.workspace = true 4 | version.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dev-dependencies] 16 | alloy.workspace = true 17 | 18 | eyre.workspace = true 19 | futures-util.workspace = true 20 | tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } 21 | -------------------------------------------------------------------------------- /examples/queries/examples/query_contract_storage.rs: -------------------------------------------------------------------------------- 1 | //! Example of querying contract storage from the Ethereum network. 2 | 3 | use alloy::{ 4 | primitives::{address, U256}, 5 | providers::{Provider, ProviderBuilder}, 6 | }; 7 | use eyre::Result; 8 | 9 | #[tokio::main] 10 | async fn main() -> Result<()> { 11 | // Create a provider. 12 | let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc".parse()?; 13 | let provider = ProviderBuilder::new().connect_http(rpc_url); 14 | 15 | // Get storage slot 0 from the Uniswap V3 USDC-ETH pool on Ethereum mainnet. 16 | let pool_address = address!("88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"); 17 | let storage_slot = U256::from(0); 18 | // The provider calls the RPC at the latest block by default. A block can exlpicitly be set 19 | // using `.block()`. 20 | let storage = provider.get_storage_at(pool_address, storage_slot).await?; 21 | 22 | println!("Slot 0: {storage:?}"); 23 | 24 | Ok(()) 25 | } 26 | -------------------------------------------------------------------------------- /examples/queries/examples/query_deployed_bytecode.rs: -------------------------------------------------------------------------------- 1 | //! Example of querying deployed bytecode of a contract on the Ethereum network. 2 | 3 | use alloy::{ 4 | primitives::address, 5 | providers::{Provider, ProviderBuilder}, 6 | }; 7 | use eyre::Result; 8 | 9 | #[tokio::main] 10 | async fn main() -> Result<()> { 11 | // Create a provider. 12 | let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc".parse()?; 13 | let provider = ProviderBuilder::new().connect_http(rpc_url); 14 | 15 | // Get the bytecode of the Uniswap V3 USDC-ETH pool on Ethereum mainnet. 16 | let pool_address = address!("88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"); 17 | let bytecode = provider.get_code_at(pool_address).await?; 18 | 19 | println!("Bytecode: {bytecode:?}"); 20 | 21 | Ok(()) 22 | } 23 | -------------------------------------------------------------------------------- /examples/queries/examples/query_logs.rs: -------------------------------------------------------------------------------- 1 | //! Example of querying logs from the Ethereum network. 2 | 3 | use alloy::{ 4 | primitives::{address, b256}, 5 | providers::{Provider, ProviderBuilder}, 6 | rpc::types::Filter, 7 | }; 8 | use eyre::Result; 9 | 10 | #[tokio::main] 11 | async fn main() -> Result<()> { 12 | // Create a provider. 13 | let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc".parse()?; 14 | let provider = ProviderBuilder::new().connect_http(rpc_url); 15 | 16 | // Get logs from the latest block 17 | let latest_block = provider.get_block_number().await?; 18 | 19 | // Create a filter to get all logs from the latest block. 20 | let filter = Filter::new().from_block(latest_block); 21 | 22 | // Get all logs from the latest block that match the filter. 23 | let logs = provider.get_logs(&filter).await?; 24 | 25 | for log in logs { 26 | println!("{log:?}"); 27 | } 28 | 29 | // Get all logs from the latest block that match the transfer event signature/topic. 30 | let transfer_event_signature = 31 | b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"); 32 | let filter = Filter::new().event_signature(transfer_event_signature).from_block(latest_block); 33 | // You could also use the event name instead of the event signature like so: 34 | // .event("Transfer(address,address,uint256)") 35 | 36 | // Get all logs from the latest block that match the filter. 37 | let logs = provider.get_logs(&filter).await?; 38 | 39 | for log in logs { 40 | println!("Transfer event: {log:?}"); 41 | } 42 | 43 | // Get all logs from the latest block emitted by the UNI token address. 44 | let uniswap_token_address = address!("1f9840a85d5aF5bf1D1762F925BDADdC4201F984"); 45 | let filter = Filter::new().address(uniswap_token_address).from_block(latest_block); 46 | 47 | // Get all logs from the latest block that match the filter. 48 | let logs = provider.get_logs(&filter).await?; 49 | 50 | for log in logs { 51 | println!("Uniswap token logs: {log:?}"); 52 | } 53 | 54 | Ok(()) 55 | } 56 | -------------------------------------------------------------------------------- /examples/sol-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples-sol-macro" 3 | publish.workspace = true 4 | version.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dev-dependencies] 16 | alloy.workspace = true 17 | 18 | eyre.workspace = true 19 | futures-util.workspace = true 20 | tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } 21 | serde.workspace = true 22 | serde_json.workspace = true 23 | -------------------------------------------------------------------------------- /examples/sol-macro/examples/abi/Colors.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "colors", 5 | "inputs": [ 6 | { 7 | "name": "", 8 | "type": "address", 9 | "internalType": "address" 10 | } 11 | ], 12 | "outputs": [ 13 | { 14 | "name": "r", 15 | "type": "uint8", 16 | "internalType": "uint8" 17 | }, 18 | { 19 | "name": "g", 20 | "type": "uint8", 21 | "internalType": "uint8" 22 | }, 23 | { 24 | "name": "b", 25 | "type": "uint8", 26 | "internalType": "uint8" 27 | } 28 | ], 29 | "stateMutability": "view" 30 | }, 31 | { 32 | "type": "function", 33 | "name": "getColor", 34 | "inputs": [ 35 | { 36 | "name": "user", 37 | "type": "address", 38 | "internalType": "address" 39 | } 40 | ], 41 | "outputs": [ 42 | { 43 | "name": "", 44 | "type": "tuple", 45 | "internalType": "struct Colors.Color", 46 | "components": [ 47 | { 48 | "name": "r", 49 | "type": "uint8", 50 | "internalType": "uint8" 51 | }, 52 | { 53 | "name": "g", 54 | "type": "uint8", 55 | "internalType": "uint8" 56 | }, 57 | { 58 | "name": "b", 59 | "type": "uint8", 60 | "internalType": "uint8" 61 | } 62 | ] 63 | } 64 | ], 65 | "stateMutability": "view" 66 | }, 67 | { 68 | "type": "function", 69 | "name": "getColorAsTuple", 70 | "inputs": [ 71 | { 72 | "name": "user", 73 | "type": "address", 74 | "internalType": "address" 75 | } 76 | ], 77 | "outputs": [ 78 | { 79 | "name": "", 80 | "type": "uint8", 81 | "internalType": "uint8" 82 | }, 83 | { 84 | "name": "", 85 | "type": "uint8", 86 | "internalType": "uint8" 87 | }, 88 | { 89 | "name": "", 90 | "type": "uint8", 91 | "internalType": "uint8" 92 | } 93 | ], 94 | "stateMutability": "view" 95 | }, 96 | { 97 | "type": "function", 98 | "name": "setColor", 99 | "inputs": [ 100 | { 101 | "name": "r", 102 | "type": "uint8", 103 | "internalType": "uint8" 104 | }, 105 | { 106 | "name": "g", 107 | "type": "uint8", 108 | "internalType": "uint8" 109 | }, 110 | { 111 | "name": "b", 112 | "type": "uint8", 113 | "internalType": "uint8" 114 | } 115 | ], 116 | "outputs": [], 117 | "stateMutability": "nonpayable" 118 | } 119 | ] -------------------------------------------------------------------------------- /examples/sol-macro/examples/all_derives.rs: -------------------------------------------------------------------------------- 1 | //! This example demonstrates the `all_derives` attribute in the `sol!` macro. 2 | //! 3 | //! The `all_derives` attribute enables the derivation of standard Rust traits. 4 | use alloy::{ 5 | primitives::{Address, U256}, 6 | sol, 7 | }; 8 | use std::hash::{DefaultHasher, Hash, Hasher}; 9 | 10 | sol! ( 11 | #![sol(all_derives)] 12 | // The `all_derives` attribute enables derivation of std rust traits such as 13 | // `Default`, `Debug`, `PartialEq`, `Eq`, and `Hash`. 14 | struct Foo { 15 | uint256 a; 16 | uint64 b; 17 | address c; 18 | } 19 | ); 20 | 21 | fn main() { 22 | // `Default` is derived. 23 | let foo = Foo::default(); 24 | let foo_bar = Foo { a: U256::from(1), b: 2, c: Address::with_last_byte(1) }; 25 | 26 | let mut foo_list = vec![foo.clone(), foo, foo_bar]; 27 | 28 | // `Debug` derived as well. 29 | println!("Initial foo_list: {foo_list:?}"); 30 | 31 | // `PartialEq` is derived, enabling us to apply `.dedup()`. 32 | foo_list.dedup(); 33 | assert_eq!(foo_list.len(), 2); 34 | 35 | // `Hash` is derived, enabling us to apply `.hash()`. 36 | let baz = Foo { a: U256::from(1), b: 2, c: Address::with_last_byte(1) }; 37 | let mut hasher = DefaultHasher::default(); 38 | baz.hash(&mut hasher); 39 | let hash = hasher.finish(); 40 | println!("Hash of baz: {hash}"); 41 | } 42 | -------------------------------------------------------------------------------- /examples/sol-macro/examples/decode_returns.rs: -------------------------------------------------------------------------------- 1 | //! Example showing how to decode return values from a call to a contract using the `sol!` macro. 2 | 3 | use alloy::{ 4 | hex, 5 | primitives::{Uint, I256, U256}, 6 | sol, 7 | sol_types::SolCall, 8 | }; 9 | use eyre::Result; 10 | 11 | // Codegen from excerpt of Chainlink Aggregator interface. 12 | // See: https://etherscan.io/address/0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419#code 13 | sol!( 14 | #[allow(missing_docs)] 15 | #[derive(Debug, PartialEq, Eq)] 16 | function getRoundData(uint80 _roundId) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); 17 | ); 18 | 19 | fn main() -> Result<()> { 20 | let result = getRoundDataCall::abi_decode_returns(&hex!( 21 | "0000000000000000000000000000000000000000000000060000000000004716 22 | 00000000000000000000000000000000000000000000000000000051faad1c80 23 | 000000000000000000000000000000000000000000000000000000006669627b 24 | 000000000000000000000000000000000000000000000000000000006669627b 25 | 0000000000000000000000000000000000000000000000060000000000004716" 26 | )); 27 | 28 | assert_eq!( 29 | result, 30 | Ok(getRoundDataReturn { 31 | roundId: Uint::<80, 2>::from(110680464442257327894_u128), 32 | answer: I256::from_dec_str("352098000000")?, 33 | startedAt: U256::from(1718182523), 34 | updatedAt: U256::from(1718182523), 35 | answeredInRound: Uint::<80, 2>::from(110680464442257327894_u128), 36 | }) 37 | ); 38 | 39 | Ok(()) 40 | } 41 | -------------------------------------------------------------------------------- /examples/sol-macro/examples/extra_derives.rs: -------------------------------------------------------------------------------- 1 | //! This example shows how to apply trait derivations globally by specifying the path. 2 | //! 3 | //! While the `all_derives` attribute is useful for deriving standard Rust traits such as Debug, 4 | //! `PartialEq`, Default etc., the `extra_derives` attribute allows us to derive other useful traits 5 | //! by specifying their path. 6 | //! 7 | //! In this example, we'll derive `serde::Serialize` and `serde::Deserialize` for the types defined 8 | //! in the `sol!` macro. 9 | 10 | use alloy::sol; 11 | 12 | sol!( 13 | // `all_derives` - derives standard Rust traits. 14 | #![sol(all_derives)] 15 | // `extra_derives` - derives additional traits by specifying their path. 16 | #![sol(extra_derives(serde::Serialize, serde::Deserialize))] 17 | Colors, 18 | "examples/abi/Colors.json", 19 | ); 20 | 21 | fn main() -> eyre::Result<()> { 22 | let color_struct = Colors::Color { r: 255, ..Default::default() }; 23 | 24 | // serde::Serialize is derived for types passed to the `sol!` macro. 25 | let json = serde_json::to_string_pretty(&color_struct)?; 26 | println!("{json}"); 27 | 28 | // serde::Deserialize is derived for all types in the abi. 29 | let deserialized: Colors::Color = serde_json::from_str(&json)?; 30 | println!("{deserialized:?}"); 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /examples/sol-macro/examples/structs_enums.rs: -------------------------------------------------------------------------------- 1 | //! Example showing how to use the `sol!` macro to generate Rust bindings for Solidity structs and 2 | //! enums. 3 | 4 | use alloy::{primitives::U256, sol}; 5 | use eyre::Result; 6 | 7 | // Generates Rust bindings for Solidity structs, enums and type aliases. 8 | sol! { 9 | #[allow(missing_docs)] 10 | #[derive(Debug)] 11 | /// Foo 12 | struct Foo { 13 | uint256 a; 14 | uint64 b; 15 | Bar greater; 16 | } 17 | 18 | #[allow(missing_docs)] 19 | #[derive(Debug)] 20 | /// Bar 21 | enum Bar { 22 | A, 23 | B, 24 | } 25 | } 26 | 27 | fn main() -> Result<()> { 28 | // Create an instance of the struct. 29 | let foo = Foo { a: U256::from(1), b: 2_u64, greater: Bar::A }; 30 | 31 | println!("{foo:?}"); 32 | 33 | Ok(()) 34 | } 35 | -------------------------------------------------------------------------------- /examples/sol-macro/examples/user_defined_types.rs: -------------------------------------------------------------------------------- 1 | //! Example showing defining user defined value types and type aliases using the `sol!` macro. 2 | 3 | use alloy::{ 4 | primitives::{Address, U256}, 5 | sol, 6 | sol_types::SolType, 7 | }; 8 | use eyre::Result; 9 | 10 | // Type definition: generates a new struct that implements `SolType` 11 | sol! { 12 | /// Equivalent to `struct CustomType(U256)` in Rust 13 | type CustomType is uint256; 14 | } 15 | 16 | // Type aliases 17 | type Bytes32 = sol! { bytes32 }; 18 | 19 | // This is equivalent to the following: 20 | // type B32 = alloy_sol_types::sol_data::FixedBytes<32>; 21 | 22 | // User defined types 23 | type CustomArrayOf = sol! { T[] }; 24 | type CustomTuple = sol! { tuple(address, bytes, string) }; 25 | 26 | fn main() -> Result<()> { 27 | let _b32_type = Bytes32::abi_encode(&[0; 32]); 28 | 29 | let _custom_type = CustomType(U256::from(1)); 30 | 31 | let _custom_array_of_type = CustomArrayOf::::abi_encode(&vec![true, false]); 32 | 33 | let _custom_tuple_type = 34 | CustomTuple::abi_encode(&(Address::ZERO, vec![0; 32], "hello".to_string())); 35 | 36 | Ok(()) 37 | } 38 | -------------------------------------------------------------------------------- /examples/subscriptions/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples-subscriptions" 3 | publish.workspace = true 4 | version.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dev-dependencies] 16 | alloy.workspace = true 17 | 18 | eyre.workspace = true 19 | futures-util.workspace = true 20 | tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } 21 | -------------------------------------------------------------------------------- /examples/subscriptions/examples/subscribe_all_logs.rs: -------------------------------------------------------------------------------- 1 | //! Example of subscribing and listening for all contract events by `WebSocket` subscription. 2 | 3 | use alloy::{ 4 | primitives::address, 5 | providers::{Provider, ProviderBuilder, WsConnect}, 6 | rpc::types::{BlockNumberOrTag, Filter}, 7 | sol, 8 | sol_types::SolEvent, 9 | }; 10 | use eyre::Result; 11 | use futures_util::stream::StreamExt; 12 | 13 | // Codegen from ABI file to interact with the contract. 14 | sol!( 15 | #[allow(missing_docs)] 16 | #[sol(rpc)] 17 | IWETH9, 18 | "examples/abi/IWETH9.json" 19 | ); 20 | 21 | #[tokio::main] 22 | async fn main() -> Result<()> { 23 | // Create the provider. 24 | let rpc_url = "wss://eth-mainnet.g.alchemy.com/v2/your-api-key"; 25 | let ws = WsConnect::new(rpc_url); 26 | let provider = ProviderBuilder::new().connect_ws(ws).await?; 27 | 28 | // Create a filter to watch for all WETH9 events. 29 | let weth9_token_address = address!("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"); 30 | let filter = Filter::new() 31 | // By NOT specifying an `event` or `event_signature` we listen to ALL events of the 32 | // contract. 33 | .address(weth9_token_address) 34 | .from_block(BlockNumberOrTag::Latest); 35 | 36 | // Subscribe to logs. 37 | let sub = provider.subscribe_logs(&filter).await?; 38 | let mut stream = sub.into_stream(); 39 | 40 | while let Some(log) = stream.next().await { 41 | // Match on topic 0, the hash of the signature of the event. 42 | match log.topic0() { 43 | // Match the `Approval(address,address,uint256)` event. 44 | Some(&IWETH9::Approval::SIGNATURE_HASH) => { 45 | let IWETH9::Approval { src, guy, wad } = log.log_decode()?.inner.data; 46 | println!("Approval from {src} to {guy} of value {wad}"); 47 | } 48 | // Match the `Transfer(address,address,uint256)` event. 49 | Some(&IWETH9::Transfer::SIGNATURE_HASH) => { 50 | let IWETH9::Transfer { src, dst, wad } = log.log_decode()?.inner.data; 51 | println!("Transfer from {src} to {dst} of value {wad}"); 52 | } 53 | // WETH9's `Deposit(address,uint256)` and `Withdrawal(address,uint256)` events are not 54 | // handled here. 55 | _ => (), 56 | } 57 | } 58 | 59 | Ok(()) 60 | } 61 | -------------------------------------------------------------------------------- /examples/subscriptions/examples/subscribe_blocks.rs: -------------------------------------------------------------------------------- 1 | //! Example of subscribing to blocks and watching block headers by polling. 2 | 3 | use alloy::{ 4 | node_bindings::Anvil, 5 | providers::{Provider, ProviderBuilder, WsConnect}, 6 | }; 7 | use eyre::Result; 8 | use futures_util::{stream, StreamExt}; 9 | 10 | #[tokio::main] 11 | async fn main() -> Result<()> { 12 | // Spin up a local Anvil node. 13 | // Ensure `anvil` is available in $PATH. 14 | let anvil = Anvil::new().block_time(1).try_spawn()?; 15 | 16 | // Create a provider. 17 | let ws = WsConnect::new(anvil.ws_endpoint()); 18 | let provider = ProviderBuilder::new().connect_ws(ws).await?; 19 | 20 | // Subscribe to block headers. 21 | let subscription = provider.subscribe_blocks().await?; 22 | let mut stream = subscription.into_stream().take(2); 23 | 24 | while let Some(header) = stream.next().await { 25 | println!("Received block number: {}", header.number); 26 | } 27 | 28 | // Poll for block headers. 29 | let poller = provider.watch_blocks().await?; 30 | let mut stream = poller.into_stream().flat_map(stream::iter).take(2); 31 | 32 | while let Some(block_hash) = stream.next().await { 33 | println!("Polled for block header: {block_hash:?}"); 34 | } 35 | 36 | Ok(()) 37 | } 38 | -------------------------------------------------------------------------------- /examples/subscriptions/examples/subscribe_logs.rs: -------------------------------------------------------------------------------- 1 | //! Example of subscribing and listening for specific contract events by `WebSocket` subscription. 2 | 3 | use alloy::{ 4 | primitives::address, 5 | providers::{Provider, ProviderBuilder, WsConnect}, 6 | rpc::types::{BlockNumberOrTag, Filter}, 7 | }; 8 | use eyre::Result; 9 | use futures_util::stream::StreamExt; 10 | 11 | #[tokio::main] 12 | async fn main() -> Result<()> { 13 | // Create the provider. 14 | let rpc_url = "wss://eth-mainnet.g.alchemy.com/v2/your-api-key"; 15 | let ws = WsConnect::new(rpc_url); 16 | let provider = ProviderBuilder::new().connect_ws(ws).await?; 17 | 18 | // Create a filter to watch for UNI token transfers. 19 | let uniswap_token_address = address!("1f9840a85d5aF5bf1D1762F925BDADdC4201F984"); 20 | let filter = Filter::new() 21 | .address(uniswap_token_address) 22 | // By specifying an `event` or `event_signature` we listen for a specific event of the 23 | // contract. In this case the `Transfer(address,address,uint256)` event. 24 | .event("Transfer(address,address,uint256)") 25 | .from_block(BlockNumberOrTag::Latest); 26 | 27 | // Subscribe to logs. 28 | let sub = provider.subscribe_logs(&filter).await?; 29 | let mut stream = sub.into_stream(); 30 | 31 | while let Some(log) = stream.next().await { 32 | println!("Uniswap token logs: {log:?}"); 33 | } 34 | 35 | Ok(()) 36 | } 37 | -------------------------------------------------------------------------------- /examples/subscriptions/examples/subscribe_pending_transactions.rs: -------------------------------------------------------------------------------- 1 | //! Example of subscribing and listening for pending transactions in the public mempool by 2 | //! `WebSocket` subscription. 3 | 4 | use alloy::providers::{Provider, ProviderBuilder, WsConnect}; 5 | use eyre::Result; 6 | use futures_util::StreamExt; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<()> { 10 | // Create the provider. 11 | let rpc_url = "wss://eth-mainnet.g.alchemy.com/v2/your-api-key"; 12 | let ws = WsConnect::new(rpc_url); 13 | let provider = ProviderBuilder::new().connect_ws(ws).await?; 14 | 15 | // Subscribe to pending transactions. 16 | // Alteratively use `subscribe_full_pending_transactions` to get the full transaction details 17 | // directly if supported by the RPC provider. 18 | let sub = provider.subscribe_pending_transactions().await?; 19 | 20 | // Wait and take the next 3 transactions. 21 | let mut stream = sub.into_stream().take(3); 22 | 23 | println!("Awaiting pending transactions..."); 24 | 25 | // Take the stream and print the pending transaction. 26 | let handle = tokio::spawn(async move { 27 | while let Some(tx_hash) = stream.next().await { 28 | // Get the transaction details. 29 | if let Ok(tx) = provider.get_transaction_by_hash(tx_hash).await { 30 | println!("Transaction details: {tx:#?}"); 31 | } 32 | } 33 | }); 34 | 35 | handle.await?; 36 | 37 | Ok(()) 38 | } 39 | -------------------------------------------------------------------------------- /examples/transactions/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples-transactions" 3 | publish.workspace = true 4 | version.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dev-dependencies] 16 | alloy = { workspace = true, features = ["eip712"]} 17 | 18 | eyre.workspace = true 19 | tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } 20 | -------------------------------------------------------------------------------- /examples/transactions/examples/debug_trace_call_many.rs: -------------------------------------------------------------------------------- 1 | //! Example of how to trace a transaction using `debug_trace_call_many`. 2 | 3 | use alloy::{ 4 | network::TransactionBuilder, 5 | node_bindings::Reth, 6 | primitives::{address, U256}, 7 | providers::{ext::DebugApi, ProviderBuilder}, 8 | rpc::types::{ 9 | trace::geth::GethDebugTracingCallOptions, Bundle, StateContext, TransactionRequest, 10 | }, 11 | }; 12 | use eyre::Result; 13 | 14 | #[tokio::main] 15 | async fn main() -> Result<()> { 16 | // Spin up a local Reth node. 17 | // Ensure `reth` is available in $PATH. 18 | let reth = Reth::new().dev().disable_discovery().instance(1).spawn(); 19 | let provider = ProviderBuilder::new().connect_http(reth.endpoint().parse()?); 20 | 21 | // Get users, these have allocated balances in the dev genesis block. 22 | let alice = address!("70997970C51812dc3A010C7d01b50e0d17dc79C8"); 23 | let bob = address!("3C44CdDdB6a900fa2b585dd299e03d12FA4293BC"); 24 | let charlie = address!("90F79bf6EB2c4f870365E785982E1f101E93b906"); 25 | let dan = address!("15d34AAf54267DB7D7c367839AAf71A00a2C6A65"); 26 | 27 | // Define transactions. 28 | let tx1 = 29 | TransactionRequest::default().with_from(alice).with_to(bob).with_value(U256::from(150)); 30 | let tx2 = 31 | TransactionRequest::default().with_from(charlie).with_to(dan).with_value(U256::from(250)); 32 | 33 | // Create the bundle of transactions. 34 | let bundles = vec![Bundle { transactions: vec![tx1, tx2], block_override: None }]; 35 | 36 | // Define the state context and trace option. 37 | let state_context = StateContext::default(); 38 | let trace_options = GethDebugTracingCallOptions::default(); 39 | 40 | // Call `debug_trace_call_many` on the provider. 41 | let result = provider.debug_trace_call_many(bundles, state_context, trace_options).await; 42 | 43 | // Print the trace results. 44 | match result { 45 | Ok(traces) => { 46 | println!("Traces:\n{traces:?}"); 47 | } 48 | Err(err) => { 49 | println!("Error tracing transactions: {err:?}"); 50 | } 51 | } 52 | 53 | Ok(()) 54 | } 55 | -------------------------------------------------------------------------------- /examples/transactions/examples/decode_input.rs: -------------------------------------------------------------------------------- 1 | //! Example of how to decode the input of a transaction. 2 | 3 | use alloy::{primitives::hex, sol, sol_types::SolCall}; 4 | use eyre::Result; 5 | 6 | // Codegen from excerpt of Uniswap V2 Router interface. 7 | // See: https://docs.uniswap.org/contracts/v2/reference/smart-contracts/router-02 8 | sol!( 9 | #[allow(missing_docs)] 10 | function swapExactTokensForTokens( 11 | uint256 amountIn, 12 | uint256 amountOutMin, 13 | address[] calldata path, 14 | address to, 15 | uint256 deadline 16 | ) external returns (uint256[] memory amounts); 17 | ); 18 | 19 | #[tokio::main] 20 | async fn main() -> Result<()> { 21 | println!("Decoding https://etherscan.io/tx/0xd1b449d8b1552156957309bffb988924569de34fbf21b51e7af31070cc80fe9a"); 22 | 23 | let input = "0x38ed173900000000000000000000000000000000000000000001a717cc0a3e4f84c00000000000000000000000000000000000000000000000000000000000000283568400000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000201f129111c60401630932d9f9811bd5b5fff34e000000000000000000000000000000000000000000000000000000006227723d000000000000000000000000000000000000000000000000000000000000000200000000000000000000000095ad61b0a150d79219dcf64e1e6cc01f0b64c4ce000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7"; 24 | let input = hex::decode(input)?; 25 | 26 | // Decode the input using the generated `swapExactTokensForTokens` bindings. 27 | let decoded = swapExactTokensForTokensCall::abi_decode(&input); 28 | 29 | match decoded { 30 | Ok(decoded) => { 31 | let path = decoded.path; 32 | 33 | println!( 34 | "Swap {} of token {} to {} of token {}", 35 | decoded.amountIn, 36 | path.first().expect("Path is empty"), 37 | decoded.amountOutMin, 38 | path.last().expect("Path is empty") 39 | ); 40 | } 41 | Err(e) => { 42 | println!("Error decoding input: {e:?}"); 43 | } 44 | } 45 | 46 | Ok(()) 47 | } 48 | -------------------------------------------------------------------------------- /examples/transactions/examples/decode_receipt_log.rs: -------------------------------------------------------------------------------- 1 | //! Example depicting how to decode logs present in a transaction receipt. 2 | 3 | use alloy::{providers::ProviderBuilder, sol}; 4 | 5 | sol! { 6 | #[sol(rpc, bytecode = "6080604052348015600e575f80fd5b506101858061001c5f395ff3fe608060405234801561000f575f80fd5b506004361061003f575f3560e01c806306661abd146100435780632baeceb71461005d578063d09de08a14610067575b5f80fd5b61004b5f5481565b60405190815260200160405180910390f35b61006561006f565b005b6100656100c7565b5f545f0361007957565b60015f8082825461008a9190610123565b90915550505f546040519081527f32814a5bdfd1b8c3d76c49c54e043d6e8aa93d197a09e16599b567135503f748906020015b60405180910390a1565b60015f808282546100d8919061013c565b90915550505f546040519081527f51af157c2eee40f68107a47a49c32fbbeb0a3c9e5cd37aa56e88e6be92368a81906020016100bd565b634e487b7160e01b5f52601160045260245ffd5b818103818111156101365761013661010f565b92915050565b808201808211156101365761013661010f56fea2646970667358221220d955d2934ffc8ca69c62bc3b116ad64fca0144d8bd6cf5a635ae1954025d810c64736f6c63430008190033")] 7 | contract Counter { 8 | uint256 public count; 9 | 10 | event Increment(uint256 value); 11 | event Decrement(uint256 value); 12 | 13 | function increment() public { 14 | count += 1; 15 | emit Increment(count); 16 | } 17 | 18 | function decrement() public { 19 | if (count == 0) return; 20 | count -= 1; 21 | emit Decrement(count); 22 | } 23 | } 24 | } 25 | 26 | #[tokio::main] 27 | async fn main() -> eyre::Result<()> { 28 | // Create an AnvilProvider 29 | // Ensure `anvil` is available in $PATH. 30 | let provider = ProviderBuilder::new().connect_anvil_with_wallet(); 31 | 32 | // Deploy the `Counter` contract. 33 | let counter = Counter::deploy(&provider).await?; 34 | 35 | // Send a transaction to increment the counter. 36 | let increment = counter.increment().send().await?; 37 | 38 | // Get the receipt of the transaction. 39 | let receipt = increment.get_receipt().await?; 40 | 41 | // Decode the `Increment` event log. 42 | // Returns None, if there were no logs emitted or if the emitted logs cannot be decoded as 43 | // `Increment`. 44 | let maybe_log = receipt.decoded_log::(); 45 | 46 | let Some(increment_log) = maybe_log else { eyre::bail!("Increment not emitted") }; 47 | 48 | assert_eq!(increment_log.address, *counter.address()); 49 | let Counter::Increment { value } = increment_log.data; 50 | println!("Incremented value: {value}"); 51 | 52 | // Attempt to decode as a `Decrement` event log. 53 | let decrement_log = receipt.decoded_log::(); 54 | 55 | // None, as there is no `Decrement` event emitted. 56 | assert!(decrement_log.is_none()); 57 | 58 | Ok(()) 59 | } 60 | -------------------------------------------------------------------------------- /examples/transactions/examples/encode_decode_eip1559.rs: -------------------------------------------------------------------------------- 1 | //! Example showing how to encode and decode an [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) transaction. 2 | 3 | use alloy::{ 4 | consensus::{SignableTransaction, TxEip1559}, 5 | eips::eip2930::AccessList, 6 | primitives::{address, b256, hex, Signature, TxKind, U256}, 7 | }; 8 | use eyre::Result; 9 | 10 | #[tokio::main] 11 | async fn main() -> Result<()> { 12 | // EIP1559 transaction: 13 | let tx_hash = b256!("0ec0b6a2df4d87424e5f6ad2a654e27aaeb7dac20ae9e8385cc09087ad532ee0"); 14 | 15 | // Signer of the transaction. 16 | let signer = address!("DD6B8b3dC6B7AD97db52F08a275FF4483e024CEa"); 17 | 18 | // Construct the EIP-1559 transaction. 19 | let tx = TxEip1559 { 20 | chain_id: 1, 21 | nonce: 0x42, 22 | gas_limit: 44386, 23 | to: TxKind::Call( address!("6069a6c32cf691f5982febae4faf8a6f3ab2f0f6")), 24 | value: U256::from(0_u64), 25 | input: hex!("a22cb4650000000000000000000000005eee75727d804a2b13038928d36f8b188945a57a0000000000000000000000000000000000000000000000000000000000000000").into(), 26 | max_fee_per_gas: 0x4a817c800, 27 | max_priority_fee_per_gas: 0x3b9aca00, 28 | access_list: AccessList::default(), 29 | }; 30 | 31 | // Construct the signature of the transaction. 32 | let signature = Signature::from_scalars_and_parity( 33 | b256!("840cfc572845f5786e702984c2a582528cad4b49b2a10b9db1be7fca90058565"), 34 | b256!("25e7109ceb98168d95b09b18bbf6b685130e0562f233877d492b94eee0c5b6d1"), 35 | false, 36 | ); 37 | 38 | // Convert the transaction into a signed transaction. 39 | let signed_tx = tx.into_signed(signature); 40 | assert_eq!(*signed_tx.hash(), tx_hash); 41 | 42 | // Recover the signer from the signed transaction to ensure it matches the expected signer. 43 | let recovered_signer = signed_tx.recover_signer()?; 44 | assert_eq!(recovered_signer, signer); 45 | 46 | Ok(()) 47 | } 48 | -------------------------------------------------------------------------------- /examples/transactions/examples/gas_price_usd.rs: -------------------------------------------------------------------------------- 1 | //! Example of how to get the gas price in USD using the Chainlink ETH/USD feed. 2 | 3 | use alloy::{ 4 | network::TransactionBuilder, 5 | primitives::{address, utils::format_units, Address, Bytes, U256}, 6 | providers::{Provider, ProviderBuilder}, 7 | rpc::types::TransactionRequest, 8 | sol, 9 | sol_types::SolCall, 10 | }; 11 | use eyre::Result; 12 | use std::str::FromStr; 13 | 14 | const ETH_USD_FEED: Address = address!("5f4eC3Df9cbd43714FE2740f5E3616155c5b8419"); 15 | const ETH_USD_FEED_DECIMALS: u8 = 8; 16 | const ETH_DECIMALS: u32 = 18; 17 | 18 | // Codegen from excerpt of Chainlink Aggregator interface. 19 | // See: https://etherscan.io/address/0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419#code 20 | sol!( 21 | #[allow(missing_docs)] 22 | function latestAnswer() external view returns (int256); 23 | ); 24 | 25 | #[tokio::main] 26 | async fn main() -> Result<()> { 27 | // Spin up a forked Anvil node. 28 | // Ensure `anvil` is available in $PATH. 29 | let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc"; 30 | let provider = ProviderBuilder::new().connect_anvil_with_config(|anvil| anvil.fork(rpc_url)); 31 | 32 | // Create a call to get the latest answer from the Chainlink ETH/USD feed. 33 | let call = latestAnswerCall {}.abi_encode(); 34 | let input = Bytes::from(call); 35 | 36 | // Call the Chainlink ETH/USD feed contract. 37 | let tx = TransactionRequest::default().with_to(ETH_USD_FEED).with_input(input); 38 | 39 | let response = provider.call(tx).await?; 40 | let result = U256::from_str(&response.to_string())?; 41 | 42 | // Get the gas price of the network. 43 | let wei_per_gas = provider.get_gas_price().await?; 44 | 45 | // Convert the gas price to Gwei and USD. 46 | let gwei = format_units(wei_per_gas, "gwei")?.parse::()?; 47 | let usd = get_usd_value(wei_per_gas, result)?; 48 | 49 | println!("Gas price in Gwei: {gwei}"); 50 | println!("Gas price in USD: {usd}"); 51 | 52 | Ok(()) 53 | } 54 | 55 | fn get_usd_value(amount: u128, price_usd: U256) -> Result { 56 | let base = U256::from(10).pow(U256::from(ETH_DECIMALS)); 57 | let value = U256::from(amount) * price_usd / base; 58 | let formatted = format_units(value, ETH_USD_FEED_DECIMALS)?.parse::()?; 59 | 60 | Ok(formatted) 61 | } 62 | -------------------------------------------------------------------------------- /examples/transactions/examples/send_eip1559_transaction.rs: -------------------------------------------------------------------------------- 1 | //! Example showing how to send an [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) transaction. 2 | 3 | use alloy::{ 4 | network::TransactionBuilder, 5 | primitives::U256, 6 | providers::{Provider, ProviderBuilder}, 7 | rpc::types::TransactionRequest, 8 | }; 9 | use eyre::Result; 10 | 11 | #[tokio::main] 12 | async fn main() -> Result<()> { 13 | // Spin up a local Anvil node. 14 | // Ensure `anvil` is available in $PATH. 15 | let provider = ProviderBuilder::new().connect_anvil(); 16 | 17 | // Create two users, Alice and Bob. 18 | let accounts = provider.get_accounts().await?; 19 | let alice = accounts[0]; 20 | let bob = accounts[1]; 21 | 22 | // Build a transaction to send 100 wei from Alice to Bob. 23 | // The `from` field is automatically filled to the first signer's address (Alice). 24 | let tx = TransactionRequest::default() 25 | .with_to(bob) 26 | .with_nonce(0) 27 | .with_chain_id(provider.get_chain_id().await?) 28 | .with_value(U256::from(100)) 29 | .with_gas_limit(21_000) 30 | .with_max_priority_fee_per_gas(1_000_000_000) 31 | .with_max_fee_per_gas(20_000_000_000); 32 | 33 | // Send the transaction and wait for the broadcast. 34 | let pending_tx = provider.send_transaction(tx).await?; 35 | 36 | println!("Pending transaction... {}", pending_tx.tx_hash()); 37 | 38 | // Wait for the transaction to be included and get the receipt. 39 | let receipt = pending_tx.get_receipt().await?; 40 | 41 | println!( 42 | "Transaction included in block {}", 43 | receipt.block_number.expect("Failed to get block number") 44 | ); 45 | 46 | assert_eq!(receipt.from, alice); 47 | assert_eq!(receipt.to, Some(bob)); 48 | 49 | Ok(()) 50 | } 51 | -------------------------------------------------------------------------------- /examples/transactions/examples/send_eip4844_transaction.rs: -------------------------------------------------------------------------------- 1 | //! Example showing how to send an [EIP-4844](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md) transaction. 2 | 3 | use alloy::{ 4 | consensus::{SidecarBuilder, SimpleCoder}, 5 | eips::eip4844::DATA_GAS_PER_BLOB, 6 | network::{TransactionBuilder, TransactionBuilder4844}, 7 | providers::{Provider, ProviderBuilder}, 8 | rpc::types::TransactionRequest, 9 | }; 10 | use eyre::Result; 11 | 12 | #[tokio::main] 13 | async fn main() -> Result<()> { 14 | // Spin up a local Anvil node with the Cancun hardfork enabled. 15 | // Ensure `anvil` is available in $PATH. 16 | let provider = ProviderBuilder::new() 17 | .connect_anvil_with_wallet_and_config(|anvil| anvil.args(["--hardfork", "cancun"]))?; 18 | 19 | // Create two users, Alice and Bob. 20 | let accounts = provider.get_accounts().await?; 21 | let alice = accounts[0]; 22 | let bob = accounts[1]; 23 | 24 | // Create a sidecar with some data. 25 | let sidecar: SidecarBuilder = SidecarBuilder::from_slice(b"Blobs are fun!"); 26 | let sidecar = sidecar.build()?; 27 | 28 | // Build a transaction to send the sidecar from Alice to Bob. 29 | // The `from` field is automatically filled to the first signer's address (Alice). 30 | let tx = TransactionRequest::default().with_to(bob).with_blob_sidecar(sidecar); 31 | 32 | // Send the transaction and wait for the broadcast. 33 | let pending_tx = provider.send_transaction(tx).await?; 34 | 35 | println!("Pending transaction... {}", pending_tx.tx_hash()); 36 | 37 | // Wait for the transaction to be included and get the receipt. 38 | let receipt = pending_tx.get_receipt().await?; 39 | 40 | println!( 41 | "Transaction included in block {}", 42 | receipt.block_number.expect("Failed to get block number") 43 | ); 44 | 45 | assert_eq!(receipt.from, alice); 46 | assert_eq!(receipt.to, Some(bob)); 47 | assert_eq!( 48 | receipt.blob_gas_used.expect("Expected to be EIP-4844 transaction"), 49 | DATA_GAS_PER_BLOB 50 | ); 51 | 52 | Ok(()) 53 | } 54 | -------------------------------------------------------------------------------- /examples/transactions/examples/send_legacy_transaction.rs: -------------------------------------------------------------------------------- 1 | //! Example showing how to send a legacy transaction. 2 | 3 | use alloy::{ 4 | network::TransactionBuilder, 5 | primitives::U256, 6 | providers::{Provider, ProviderBuilder}, 7 | rpc::types::TransactionRequest, 8 | }; 9 | use eyre::Result; 10 | 11 | #[tokio::main] 12 | async fn main() -> Result<()> { 13 | // // Spin up a local Anvil node. 14 | // // Ensure `anvil` is available in $PATH. 15 | let provider = ProviderBuilder::new().connect_anvil(); 16 | 17 | // Create two users, Alice and Bob. 18 | let accounts = provider.get_accounts().await?; 19 | let alice = accounts[0]; 20 | let bob = accounts[1]; 21 | 22 | // Build a transaction to send 100 wei from Alice to Bob. 23 | // The `from` field is automatically filled to the first signer's address (Alice). 24 | let tx = TransactionRequest::default() 25 | .with_to(bob) 26 | .with_nonce(0) 27 | .with_value(U256::from(100)) 28 | .with_gas_price(20_000_000_000) 29 | .with_gas_limit(21_000); 30 | 31 | // Send the transaction and wait for the broadcast. 32 | let pending_tx = provider.send_transaction(tx).await?; 33 | 34 | println!("Pending transaction... {}", pending_tx.tx_hash()); 35 | 36 | // Wait for the transaction to be included and get the receipt. 37 | let receipt = pending_tx.get_receipt().await?; 38 | 39 | println!( 40 | "Transaction included in block {}", 41 | receipt.block_number.expect("Failed to get block number") 42 | ); 43 | 44 | assert_eq!(receipt.from, alice); 45 | assert_eq!(receipt.to, Some(bob)); 46 | 47 | Ok(()) 48 | } 49 | -------------------------------------------------------------------------------- /examples/transactions/examples/send_private_transaction.rs: -------------------------------------------------------------------------------- 1 | //! Example of sending a private transaction using Flashbots Protect. 2 | 3 | use alloy::{ 4 | network::{eip2718::Encodable2718, EthereumWallet, TransactionBuilder}, 5 | primitives::U256, 6 | providers::{Provider, ProviderBuilder}, 7 | rpc::types::TransactionRequest, 8 | signers::local::PrivateKeySigner, 9 | }; 10 | use eyre::Result; 11 | 12 | #[tokio::main] 13 | async fn main() -> Result<()> { 14 | // Set up the HTTP transport which is consumed by the RPC client. 15 | // 16 | // By default, Flashbots Protect transactions are only shared with the Flashbots Builder, which 17 | // builds only a subset of all Ethereum blocks. In `fast` mode, transactions are shared with 18 | // all registered builders no less than one block after they are received to increase the 19 | // number of blocks the user's transaction can be included in. 20 | // 21 | // Fast mode has 2 key differences from the default Protect experience: 22 | // - Shared with all builders: By default, Flashbots Protect transactions are only shared with 23 | // the Flashbots Builder, which builds only a subset of all Ethereum blocks. In fast mode, 24 | // transactions are shared with all registered builders no less than one block after they are 25 | // received to increase the number of blocks the user's transaction can be included in. 26 | // - Larger refund paid to validator: By default, only 10% of MEV-Share refunds are paid to 27 | // validators. In fast mode, validators receive 50% of refunds which makes it more likely that 28 | // the user’s transactions will be chosen in a given block. 29 | // 30 | // For more information, see the [Flashbots documentation](https://docs.flashbots.net/flashbots-protect/overview). 31 | // 32 | // To use `fast` mode change the URL to `https://rpc.flashbots.net/fast`. 33 | let flashbots_url = "https://rpc.flashbots.net".parse()?; 34 | 35 | // Create a provider. 36 | let provider = ProviderBuilder::new().connect_http(flashbots_url); 37 | 38 | // Create a signer from a random private key. 39 | let signer = PrivateKeySigner::random(); 40 | let wallet = EthereumWallet::from(signer); 41 | 42 | // Build a transaction to send 100 wei from Alice to Bob. 43 | // The `from` field is automatically filled to the first signer's address (Alice). 44 | let bob = PrivateKeySigner::random().address(); 45 | let tx = TransactionRequest::default() 46 | .with_to(bob) 47 | .with_nonce(0) 48 | .with_chain_id(1) 49 | .with_value(U256::from(100)) 50 | .with_gas_limit(21_000) 51 | .with_max_priority_fee_per_gas(1_000_000_000) 52 | .with_max_fee_per_gas(20_000_000_000); 53 | 54 | // Build the transaction with the provided wallet. Flashbots Protect requires the transaction to 55 | // be signed locally and send using `eth_sendRawTransaction`. 56 | let tx_envelope = tx.build(&wallet).await?; 57 | 58 | // Encode the transaction using EIP-2718 encoding. 59 | let tx_encoded = tx_envelope.encoded_2718(); 60 | 61 | // Send the raw transaction. The transaction is sent to the Flashbots relay and, if valid, will 62 | // be included in a block by a Flashbots builder. Note that the transaction request, as defined, 63 | // is invalid and will not be included in the blockchain. 64 | let pending = provider.send_raw_transaction(&tx_encoded).await?.register().await?; 65 | 66 | println!("Sent transaction: {}", pending.tx_hash()); 67 | 68 | Ok(()) 69 | } 70 | -------------------------------------------------------------------------------- /examples/transactions/examples/send_raw_transaction.rs: -------------------------------------------------------------------------------- 1 | //! Example of signing, encoding and sending a raw transaction using a wallet. 2 | 3 | use alloy::{ 4 | network::TransactionBuilder, 5 | primitives::U256, 6 | providers::{Provider, ProviderBuilder, WalletProvider}, 7 | rpc::types::TransactionRequest, 8 | }; 9 | use eyre::Result; 10 | 11 | #[tokio::main] 12 | async fn main() -> Result<()> { 13 | // Spin up a local Anvil node. 14 | // Ensure `anvil` is available in $PATH. 15 | let provider = ProviderBuilder::new().connect_anvil_with_wallet(); 16 | 17 | // Create two users, Alice and Bob. 18 | let accounts = provider.get_accounts().await?; 19 | let alice = accounts[0]; 20 | let bob = accounts[1]; 21 | 22 | // Build a transaction to send 100 wei from Alice to Bob. 23 | // The `from` field is automatically filled to the first signer's address (Alice). 24 | let tx = TransactionRequest::default() 25 | .with_to(bob) 26 | .with_nonce(0) 27 | .with_chain_id(provider.get_chain_id().await?) 28 | .with_value(U256::from(100)) 29 | .with_gas_limit(21_000) 30 | .with_max_priority_fee_per_gas(1_000_000_000) 31 | .with_max_fee_per_gas(20_000_000_000); 32 | 33 | // Build and sign the transaction using the `EthereumWallet` with the provided wallet. 34 | let tx_envelope = tx.build(&provider.wallet()).await?; 35 | 36 | // Send the raw transaction and retrieve the transaction receipt. 37 | // [Provider::send_tx_envelope] is a convenience method that encodes the transaction using 38 | // EIP-2718 encoding and broadcasts it to the network using [Provider::send_raw_transaction]. 39 | let receipt = provider.send_tx_envelope(tx_envelope).await?.get_receipt().await?; 40 | 41 | println!("Sent transaction: {}", receipt.transaction_hash); 42 | 43 | assert_eq!(receipt.from, alice); 44 | assert_eq!(receipt.to, Some(bob)); 45 | 46 | Ok(()) 47 | } 48 | -------------------------------------------------------------------------------- /examples/transactions/examples/trace_call.rs: -------------------------------------------------------------------------------- 1 | //! Example of how to trace a transaction using `trace_call`. 2 | 3 | use alloy::{ 4 | network::TransactionBuilder, 5 | primitives::{address, U256}, 6 | providers::{ext::TraceApi, ProviderBuilder}, 7 | rpc::types::TransactionRequest, 8 | }; 9 | use eyre::Result; 10 | 11 | #[tokio::main] 12 | async fn main() -> Result<()> { 13 | // Create a provider. 14 | let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc".parse()?; 15 | let provider = ProviderBuilder::new().connect_http(rpc_url); 16 | 17 | // Build a transaction to send 100 wei from Alice to Vitalik. 18 | let alice = address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266"); 19 | let vitalik = address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"); 20 | let tx = 21 | TransactionRequest::default().with_from(alice).with_to(vitalik).with_value(U256::from(100)); 22 | 23 | // Trace the transaction on top of the latest block. 24 | // Defaults to TraceType::Trace. 25 | let result = provider.trace_call(&tx).await?; 26 | 27 | println!("{:?}", result.trace); 28 | 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /examples/transactions/examples/trace_call_many.rs: -------------------------------------------------------------------------------- 1 | //! Example of how to trace a transaction using `trace_call_many`. 2 | 3 | use alloy::{ 4 | network::TransactionBuilder, 5 | node_bindings::Reth, 6 | primitives::{address, U256}, 7 | providers::{ext::TraceApi, ProviderBuilder}, 8 | rpc::types::{trace::parity::TraceType, TransactionRequest}, 9 | }; 10 | use eyre::Result; 11 | 12 | #[tokio::main] 13 | async fn main() -> Result<()> { 14 | // Spin up a local Reth node. 15 | // Ensure `reth` is available in $PATH. 16 | let reth = Reth::new().dev().disable_discovery().instance(1).spawn(); 17 | let provider = ProviderBuilder::new().connect_http(reth.endpoint().parse()?); 18 | 19 | // Get users, these have allocated balances in the dev genesis block. 20 | let alice = address!("70997970C51812dc3A010C7d01b50e0d17dc79C8"); 21 | let bob = address!("3C44CdDdB6a900fa2b585dd299e03d12FA4293BC"); 22 | let charlie = address!("90F79bf6EB2c4f870365E785982E1f101E93b906"); 23 | let dan = address!("15d34AAf54267DB7D7c367839AAf71A00a2C6A65"); 24 | 25 | // Define transactions. 26 | let tx1 = 27 | TransactionRequest::default().with_from(alice).with_to(bob).with_value(U256::from(150)); 28 | let tx2 = 29 | TransactionRequest::default().with_from(charlie).with_to(dan).with_value(U256::from(250)); 30 | 31 | // Define the trace type for the trace call list. 32 | let trace_type: &[TraceType] = &[TraceType::Trace]; 33 | 34 | // Trace the transaction on top of the latest block. 35 | let trace_call_list = &[(tx1, trace_type), (tx2, trace_type)]; 36 | 37 | let result = provider.trace_call_many(trace_call_list).await?; 38 | 39 | // Print the trace results. 40 | for (index, trace_result) in result.iter().enumerate() { 41 | println!("Trace result for transaction {index}: {trace_result:?}"); 42 | } 43 | Ok(()) 44 | } 45 | -------------------------------------------------------------------------------- /examples/transactions/examples/trace_transaction.rs: -------------------------------------------------------------------------------- 1 | //! Example of how to trace a transaction using `trace_transaction`. 2 | 3 | use alloy::{ 4 | primitives::b256, 5 | providers::{ext::DebugApi, ProviderBuilder}, 6 | rpc::types::trace::geth::{ 7 | GethDebugBuiltInTracerType, GethDebugTracerType, GethDebugTracingOptions, 8 | GethDefaultTracingOptions, 9 | }, 10 | }; 11 | use eyre::Result; 12 | 13 | #[tokio::main] 14 | async fn main() -> Result<()> { 15 | // Spin up a forked Anvil node. 16 | // Ensure `anvil` is available in $PATH. 17 | let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc"; 18 | let provider = ProviderBuilder::new().connect_anvil_with_config(|anvil| anvil.fork(rpc_url)); 19 | 20 | // Hash of the tx we want to trace. 21 | let hash = b256!("97a02abf405d36939e5b232a5d4ef5206980c5a6661845436058f30600c52df7"); 22 | 23 | // Trace with the default tracer. 24 | let default_options = GethDebugTracingOptions::default(); 25 | let result = provider.debug_trace_transaction(hash, default_options).await?; 26 | 27 | println!("DEFAULT_TRACE: {result:?}"); 28 | 29 | // Trace with built-in call tracer. 30 | let call_options = GethDebugTracingOptions { 31 | config: GethDefaultTracingOptions { 32 | disable_storage: Some(true), 33 | enable_memory: Some(false), 34 | ..Default::default() 35 | }, 36 | tracer: Some(GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::CallTracer)), 37 | ..Default::default() 38 | }; 39 | let result = provider.debug_trace_transaction(hash, call_options).await?; 40 | 41 | println!("CALL_TRACE: {result:?}"); 42 | 43 | // Trace using a custom JavaScript tracer. 44 | let js_options = GethDebugTracingOptions { 45 | tracer: Some(GethDebugTracerType::JsTracer("{data: [], fault: function(log) {}, step: function(log) { if(log.op.toString() == \"DELEGATECALL\") this.data.push(log.stack.peek(0)); }, result: function() { return this.data; }}".into())), 46 | ..Default::default() 47 | }; 48 | let result = provider.debug_trace_transaction(hash, js_options).await?; 49 | 50 | println!("JS_TRACER: {result:?}"); 51 | 52 | Ok(()) 53 | } 54 | -------------------------------------------------------------------------------- /examples/transactions/examples/transfer_erc20.rs: -------------------------------------------------------------------------------- 1 | //! Example of how to transfer ERC20 tokens from one account to another. 2 | 3 | use alloy::{ 4 | primitives::U256, 5 | providers::{Provider, ProviderBuilder}, 6 | sol, 7 | }; 8 | use eyre::Result; 9 | 10 | // Codegen from artifact. 11 | sol!( 12 | #[allow(missing_docs)] 13 | #[sol(rpc)] 14 | ERC20Example, 15 | "examples/artifacts/ERC20Example.json" 16 | ); 17 | 18 | #[tokio::main] 19 | async fn main() -> Result<()> { 20 | // Spin up a forked Anvil node. 21 | // Ensure `anvil` is available in $PATH. 22 | let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc"; 23 | let provider = 24 | ProviderBuilder::new().connect_anvil_with_wallet_and_config(|anvil| anvil.fork(rpc_url))?; 25 | 26 | // Create two users, Alice and Bob. 27 | let accounts = provider.get_accounts().await?; 28 | let alice = accounts[0]; 29 | let bob = accounts[1]; 30 | 31 | // Deploy the `ERC20Example` contract. 32 | let contract = ERC20Example::deploy(provider).await?; 33 | 34 | // Register the balances of Alice and Bob before the transfer. 35 | let alice_before_balance = contract.balanceOf(alice).call().await?; 36 | let bob_before_balance = contract.balanceOf(bob).call().await?; 37 | 38 | // Transfer and wait for inclusion. 39 | let amount = U256::from(100); 40 | let tx_hash = contract.transfer(bob, amount).send().await?.watch().await?; 41 | 42 | println!("Sent transaction: {tx_hash}"); 43 | 44 | // Register the balances of Alice and Bob after the transfer. 45 | let alice_after_balance = contract.balanceOf(alice).call().await?; 46 | let bob_after_balance = contract.balanceOf(bob).call().await?; 47 | 48 | // Check the balances of Alice and Bob after the transfer. 49 | assert_eq!(alice_before_balance - alice_after_balance, amount); 50 | assert_eq!(bob_after_balance - bob_before_balance, amount); 51 | 52 | Ok(()) 53 | } 54 | -------------------------------------------------------------------------------- /examples/transactions/examples/transfer_eth.rs: -------------------------------------------------------------------------------- 1 | //! Example of how to transfer ETH from one account to another. 2 | 3 | use alloy::{ 4 | network::TransactionBuilder, 5 | primitives::U256, 6 | providers::{Provider, ProviderBuilder}, 7 | rpc::types::TransactionRequest, 8 | }; 9 | use eyre::Result; 10 | 11 | #[tokio::main] 12 | async fn main() -> Result<()> { 13 | // Spin up a local Anvil node. 14 | // Ensure `anvil` is available in $PATH. 15 | let provider = ProviderBuilder::new().connect_anvil_with_wallet(); 16 | 17 | // Create two users, Alice and Bob. 18 | let accounts = provider.get_accounts().await?; 19 | let alice = accounts[0]; 20 | let bob = accounts[1]; 21 | 22 | // Build a transaction to send 100 wei from Alice to Bob. 23 | // The `from` field is automatically filled to the first signer's address (Alice). 24 | let tx = 25 | TransactionRequest::default().with_from(alice).with_to(bob).with_value(U256::from(100)); 26 | 27 | // Send the transaction and listen for the transaction to be included. 28 | let tx_hash = provider.send_transaction(tx).await?.watch().await?; 29 | 30 | println!("Sent transaction: {tx_hash}"); 31 | 32 | Ok(()) 33 | } 34 | -------------------------------------------------------------------------------- /examples/transactions/examples/with_access_list.rs: -------------------------------------------------------------------------------- 1 | //! Example of sending a EIP-1559 transaction with access list. 2 | 3 | use alloy::{ 4 | providers::{Provider, ProviderBuilder}, 5 | rpc::types::TransactionRequest, 6 | sol, 7 | }; 8 | use eyre::Result; 9 | 10 | // Codegen from artifact. 11 | sol!( 12 | #[allow(missing_docs)] 13 | #[sol(rpc)] 14 | SimpleStorage, 15 | "examples/artifacts/SimpleStorage.json" 16 | ); 17 | 18 | #[tokio::main] 19 | async fn main() -> Result<()> { 20 | // Spin up a local Anvil node. 21 | // Ensure `anvil` is available in $PATH. 22 | let provider = ProviderBuilder::new().connect_anvil(); 23 | 24 | // Create two users, Alice and Bob. 25 | let accounts = provider.get_accounts().await?; 26 | let alice = accounts[0]; 27 | let bob = accounts[1]; 28 | 29 | // Deploy the `SimpleStorage` contract. 30 | let contract_address = SimpleStorage::deploy_builder(provider.clone(), "initial".to_string()) 31 | .from(alice) 32 | .deploy() 33 | .await?; 34 | let contract = SimpleStorage::new(contract_address, provider.clone()); 35 | 36 | // Build a transaction to set the values of the contract. 37 | // The `from` field is automatically filled to the first signer's address (Alice). 38 | let set_value_call = contract.setValues("hello".to_string(), "world".to_string()); 39 | let calldata = set_value_call.calldata().to_owned(); 40 | let tx = TransactionRequest::default().from(bob).to(contract_address).input(calldata.into()); 41 | 42 | // Create an access list for the transaction. 43 | let access_list_with_gas_used = provider.create_access_list(&tx).await?; 44 | 45 | // Add the access list to the transaction. 46 | let tx_with_access_list = tx.access_list(access_list_with_gas_used.access_list); 47 | 48 | // Send the transaction with the access list. 49 | let tx_hash = provider.send_transaction(tx_with_access_list).await?.watch().await?; 50 | 51 | println!("Transaction hash: {tx_hash}"); 52 | 53 | // Check the value of the contract. 54 | let value = contract.getValue().call().await?; 55 | 56 | assert_eq!(value, "hello"); 57 | 58 | Ok(()) 59 | } 60 | -------------------------------------------------------------------------------- /examples/wallets/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples-wallets" 3 | publish.workspace = true 4 | version.workspace = true 5 | edition.workspace = true 6 | rust-version.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | 12 | [lints] 13 | workspace = true 14 | 15 | [dev-dependencies] 16 | alloy.workspace = true 17 | 18 | aws-config = { version = "1.6", default-features = false } 19 | aws-sdk-kms = { version = "1.68", default-features = false } 20 | eyre.workspace = true 21 | gcloud-sdk = { version = "0.27", features = [ 22 | "google-cloud-kms-v1", 23 | "google-longrunning", 24 | ] } 25 | rand = "0.8.5" 26 | serde = { workspace = true, features = ["derive"] } 27 | tempfile = "3.19" 28 | tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } 29 | -------------------------------------------------------------------------------- /examples/wallets/examples/aws_signer.rs: -------------------------------------------------------------------------------- 1 | //! Example showing how to use the AWS KMS signer. 2 | 3 | use alloy::signers::{aws::AwsSigner, Signer}; 4 | use aws_config::BehaviorVersion; 5 | use eyre::Result; 6 | 7 | #[tokio::main] 8 | async fn main() -> Result<()> { 9 | let key_id = std::env::var("AWS_KEY_ID").expect("AWS_KEY_ID not set in .env file"); 10 | 11 | let config = aws_config::load_defaults(BehaviorVersion::latest()).await; 12 | let client = aws_sdk_kms::Client::new(&config); 13 | let signer = AwsSigner::new(client, key_id, Some(1)).await?; 14 | 15 | let message = "Hello, world!"; 16 | let signature = signer.sign_message(message.as_bytes()).await?; 17 | 18 | assert_eq!(signature.recover_address_from_msg(message)?, signer.address()); 19 | 20 | Ok(()) 21 | } 22 | -------------------------------------------------------------------------------- /examples/wallets/examples/create_keystore.rs: -------------------------------------------------------------------------------- 1 | //! Example of creating a keystore file from a private key and password, and then reading it back. 2 | 3 | use std::fs::read_to_string; 4 | 5 | use alloy::{primitives::hex, signers::local::LocalSigner}; 6 | use eyre::Result; 7 | use rand::thread_rng; 8 | use tempfile::tempdir; 9 | 10 | #[tokio::main] 11 | async fn main() -> Result<()> { 12 | let dir = tempdir()?; 13 | let mut rng = thread_rng(); 14 | 15 | // Private key of Alice, the first default Anvil account. 16 | let private_key = hex!("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"); 17 | 18 | // Password to encrypt the keystore file with. 19 | let password = "test"; 20 | 21 | // Create a keystore file from the private key of Alice, returning a [Wallet] instance. 22 | let (wallet, file_path) = 23 | LocalSigner::encrypt_keystore(&dir, &mut rng, private_key, password, None)?; 24 | 25 | let keystore_file_path = dir.path().join(file_path); 26 | 27 | println!("Wrote keystore for {} to {:?}", wallet.address(), keystore_file_path); 28 | 29 | // Read the keystore file back. 30 | let recovered_wallet = LocalSigner::decrypt_keystore(keystore_file_path.clone(), password)?; 31 | 32 | println!( 33 | "Read keystore from {:?}, recovered address: {}", 34 | keystore_file_path, 35 | recovered_wallet.address() 36 | ); 37 | 38 | // Assert that the address of the original key and the recovered key are the same. 39 | assert_eq!(wallet.address(), recovered_wallet.address()); 40 | 41 | // Display the contents of the keystore file. 42 | let keystore_contents = read_to_string(keystore_file_path)?; 43 | 44 | println!("Keystore file contents: {keystore_contents:?}"); 45 | 46 | Ok(()) 47 | } 48 | -------------------------------------------------------------------------------- /examples/wallets/examples/ethereum_wallet.rs: -------------------------------------------------------------------------------- 1 | //! Demonstrates how `EthereumWallet` can use multiple different types of signers. 2 | 3 | use alloy::{ 4 | network::{EthereumWallet, TransactionBuilder, TxSigner}, 5 | node_bindings::Anvil, 6 | primitives::{address, U256}, 7 | providers::{Provider, ProviderBuilder}, 8 | rpc::types::TransactionRequest, 9 | signers::{ 10 | aws::AwsSigner, 11 | ledger::{HDPath, LedgerSigner}, 12 | local::PrivateKeySigner, 13 | }, 14 | }; 15 | use aws_config::BehaviorVersion; 16 | 17 | #[tokio::main] 18 | async fn main() -> eyre::Result<()> { 19 | // Spin up a local Anvil node. 20 | // Ensure `anvil` is available in $PATH. 21 | let anvil = Anvil::new().try_spawn()?; 22 | 23 | // Initialize your signers. 24 | 25 | // Set up a local signer. 26 | let pk_signer: PrivateKeySigner = 27 | "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse()?; 28 | let pk_addr = pk_signer.address(); 29 | 30 | // Set up a hardware wallet signer 31 | let ledger = LedgerSigner::new(HDPath::LedgerLive(0), Some(1)).await?; 32 | let ledger_addr = ledger.address(); 33 | 34 | // AWS KMS signer 35 | let key_id = std::env::var("AWS_KEY_ID").expect("AWS_KEY_ID not set in .env file"); 36 | let config = aws_config::load_defaults(BehaviorVersion::latest()).await; 37 | let client = aws_sdk_kms::Client::new(&config); 38 | let aws = AwsSigner::new(client, key_id, Some(1)).await?; 39 | let _aws_addr = aws.address(); 40 | 41 | // Initialize `EthereumWallet`. 42 | // `pk_signer` is set as the default signer. 43 | // This signer is used to sign `TransactionRequest` and `TypedTransaction` objects that do 44 | // not specify a signer address in the `from` field. 45 | let mut wallet = EthereumWallet::new(pk_signer); 46 | // Add aws and ledger signers to the wallet 47 | wallet.register_signer(aws); 48 | wallet.register_signer(ledger); 49 | 50 | // Create a provider with the `WalletFiller`. 51 | let provider = ProviderBuilder::new().wallet(wallet).connect_http(anvil.endpoint_url()); 52 | 53 | // Note that the `from` field hasn't been specified. 54 | // The wallet filler in the provider will set to it the default signer's address, which is 55 | // `pk_signer`. 56 | let tx = TransactionRequest::default() 57 | .with_to(address!("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")) 58 | .with_value(U256::from(100)); 59 | 60 | let receipt = provider.send_transaction(tx).await?.get_receipt().await?; 61 | assert_eq!(pk_addr, receipt.from); 62 | 63 | // One can hint the wallet filler about which signer to use by specifying the `from` field. 64 | // In this case, the `ledger` signer will be used to sign the transaction. 65 | let tx = TransactionRequest::default() 66 | .with_from(ledger_addr) 67 | .with_to(address!("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")) 68 | .with_value(U256::from(100)); 69 | 70 | let receipt = provider.send_transaction(tx).await?.get_receipt().await?; 71 | assert_eq!(ledger_addr, receipt.from); 72 | 73 | Ok(()) 74 | } 75 | -------------------------------------------------------------------------------- /examples/wallets/examples/gcp_signer.rs: -------------------------------------------------------------------------------- 1 | //! Example showing how to use the GCP KMS signer. 2 | 3 | use alloy::signers::{ 4 | gcp::{GcpKeyRingRef, GcpSigner, KeySpecifier}, 5 | Signer, 6 | }; 7 | use eyre::Result; 8 | use gcloud_sdk::{ 9 | google::cloud::kms::v1::key_management_service_client::KeyManagementServiceClient, GoogleApi, 10 | }; 11 | 12 | #[tokio::main] 13 | async fn main() -> Result<()> { 14 | let project_id = 15 | std::env::var("GOOGLE_PROJECT_ID").expect("GOOGLE_PROJECT_ID not set in .env file"); 16 | let location = std::env::var("GOOGLE_LOCATION").expect("GOOGLE_LOCATION not set in .env file"); 17 | let keyring = std::env::var("GOOGLE_KEYRING").expect("GOOGLE_KEYRING not set in .env file"); 18 | let key_name = std::env::var("GOOGLE_KEY_NAME").expect("GOOGLE_KEY_NAME not set in .env file"); 19 | 20 | let keyring = GcpKeyRingRef::new(&project_id, &location, &keyring); 21 | let client = GoogleApi::from_function( 22 | KeyManagementServiceClient::new, 23 | "https://cloudkms.googleapis.com", 24 | None, 25 | ) 26 | .await?; 27 | 28 | let key_version = 1; 29 | let specifier = KeySpecifier::new(keyring, &key_name, key_version); 30 | let signer = GcpSigner::new(client, specifier, Some(key_version)).await?; 31 | 32 | let message = "Hello, world!"; 33 | let signature = signer.sign_message(message.as_bytes()).await?; 34 | 35 | assert_eq!(signature.recover_address_from_msg(message)?, signer.address()); 36 | 37 | Ok(()) 38 | } 39 | -------------------------------------------------------------------------------- /examples/wallets/examples/keystore/alice.json: -------------------------------------------------------------------------------- 1 | { 2 | "crypto": { 3 | "cipher": "aes-128-ctr", 4 | "cipherparams": { 5 | "iv": "93f3de411f33a2027780f4b758fc79a7" 6 | }, 7 | "ciphertext": "92dd80b88bc29c4814ba2df782962d8a61bc8171e2da4504f7c86abf87c700a2", 8 | "kdf": "scrypt", 9 | "kdfparams": { 10 | "dklen": 32, 11 | "n": 8192, 12 | "p": 1, 13 | "r": 8, 14 | "salt": "048ddfcdbc645b3f25de25fa5e1c22e2cf66f1c842cade848b55aab8ef74b9e7" 15 | }, 16 | "mac": "b65b8441b6d778247d72d5f46a9fe5ee3311c80391f023b9e034dee0b400c091" 17 | }, 18 | "id": "fa6c4d94-1f55-4959-bdc9-ef82e59f9ae2", 19 | "version": 3 20 | } -------------------------------------------------------------------------------- /examples/wallets/examples/keystore_signer.rs: -------------------------------------------------------------------------------- 1 | //! Example of using a keystore wallet to sign and send a transaction. 2 | 3 | use std::path::PathBuf; 4 | 5 | use alloy::{ 6 | network::TransactionBuilder, 7 | primitives::{address, U256}, 8 | providers::{Provider, ProviderBuilder}, 9 | rpc::types::TransactionRequest, 10 | signers::local::LocalSigner, 11 | }; 12 | use eyre::Result; 13 | 14 | #[tokio::main] 15 | async fn main() -> Result<()> { 16 | // Password to decrypt the keystore file with. 17 | let password = "test"; 18 | 19 | // Set up signer using Alice's keystore file. 20 | // The private key belongs to Alice, the first default Anvil account. 21 | let keystore_file_path = 22 | PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?).join("examples/keystore/alice.json"); 23 | let signer = LocalSigner::decrypt_keystore(keystore_file_path, password)?; 24 | 25 | // Create a provider with the wallet. 26 | let provider = ProviderBuilder::new() 27 | .wallet(signer) 28 | .connect_anvil_with_config(|anvil| anvil.block_time(1)); 29 | 30 | // Build a transaction to send 100 wei from Alice to Vitalik. 31 | // The `from` field is automatically filled to the first signer's address (Alice). 32 | let vitalik = address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"); 33 | let tx = TransactionRequest::default().with_to(vitalik).with_value(U256::from(100)); 34 | 35 | // Send the transaction and wait for inclusion. 36 | let tx_hash = provider.send_transaction(tx).await?.watch().await?; 37 | 38 | println!("Sent transaction: {tx_hash}"); 39 | 40 | Ok(()) 41 | } 42 | -------------------------------------------------------------------------------- /examples/wallets/examples/ledger_signer.rs: -------------------------------------------------------------------------------- 1 | //! Example of signing and sending a transaction using a Ledger device. 2 | 3 | use alloy::{ 4 | network::TransactionBuilder, 5 | primitives::{address, U256}, 6 | providers::{Provider, ProviderBuilder}, 7 | rpc::types::TransactionRequest, 8 | signers::ledger::{HDPath, LedgerSigner}, 9 | }; 10 | use eyre::Result; 11 | 12 | #[tokio::main] 13 | async fn main() -> Result<()> { 14 | // Instantiate the application by acquiring a lock on the Ledger device. 15 | let signer = LedgerSigner::new(HDPath::LedgerLive(0), Some(1)).await?; 16 | 17 | // Create a provider with the wallet. 18 | let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc".parse()?; 19 | let provider = ProviderBuilder::new().wallet(signer).connect_http(rpc_url); 20 | 21 | // Build a transaction to send 100 wei from Alice to Vitalik. 22 | // The `from` field is automatically filled to the first signer's address (Alice). 23 | let vitalik = address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"); 24 | let tx = TransactionRequest::default().with_to(vitalik).with_value(U256::from(100)); 25 | 26 | // Send the transaction and wait for inclusion with 3 confirmations. 27 | let tx_hash = 28 | provider.send_transaction(tx).await?.with_required_confirmations(3).watch().await?; 29 | 30 | println!("Sent transaction: {tx_hash}"); 31 | 32 | Ok(()) 33 | } 34 | -------------------------------------------------------------------------------- /examples/wallets/examples/mnemonic_signer.rs: -------------------------------------------------------------------------------- 1 | //! Example of using `MnemonicBuilder` to access a wallet from a mnemonic phrase. 2 | 3 | use alloy::signers::local::{coins_bip39::English, MnemonicBuilder}; 4 | use eyre::Result; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<()> { 8 | let phrase = "work man father plunge mystery proud hollow address reunion sauce theory bonus"; 9 | let index = 0u32; 10 | let password = "TREZOR123"; 11 | 12 | // Access mnemonic phrase with password. 13 | // Child key at derivation path: m/44'/60'/0'/0/{index}. 14 | let wallet = MnemonicBuilder::::default() 15 | .phrase(phrase) 16 | .index(index)? 17 | // Use this if your mnemonic is encrypted. 18 | .password(password) 19 | .build()?; 20 | 21 | println!("Wallet: {}", wallet.address()); 22 | 23 | // Generate a random wallet (24 word phrase) at custom derivation path. 24 | let wallet = MnemonicBuilder::::default() 25 | .word_count(24) 26 | .derivation_path("m/44'/60'/0'/2/1")? 27 | // Optionally add this if you want the generated mnemonic to be written 28 | // to a file `.write_to(path)`. 29 | .build_random()?; 30 | 31 | println!("Random wallet: {}", wallet.address()); 32 | 33 | Ok(()) 34 | } 35 | -------------------------------------------------------------------------------- /examples/wallets/examples/private_key_signer.rs: -------------------------------------------------------------------------------- 1 | //! Example of using a local wallet to sign and send a transaction. 2 | 3 | use alloy::{ 4 | network::TransactionBuilder, 5 | node_bindings::Anvil, 6 | primitives::{address, U256}, 7 | providers::{Provider, ProviderBuilder}, 8 | rpc::types::TransactionRequest, 9 | signers::local::PrivateKeySigner, 10 | }; 11 | use eyre::Result; 12 | 13 | #[tokio::main] 14 | async fn main() -> Result<()> { 15 | // Spin up a local Anvil node. 16 | // Ensure `anvil` is available in $PATH. 17 | let anvil = Anvil::new().block_time(1).try_spawn()?; 18 | 19 | // Set up signer from the first default Anvil account (Alice). 20 | // [RISK WARNING! Writing a private key in the code file is insecure behavior.] 21 | // The following code is for testing only. Set up signer from private key, be aware of danger. 22 | // let signer: PrivateKeySigner = "".parse().expect("should parse private key"); 23 | let signer: PrivateKeySigner = anvil.keys()[0].clone().into(); 24 | 25 | // Create a provider with the wallet. 26 | let rpc_url = anvil.endpoint_url(); 27 | let provider = ProviderBuilder::new().wallet(signer).connect_http(rpc_url); 28 | 29 | // Build a transaction to send 100 wei from Alice to Vitalik. 30 | // The `from` field is automatically filled to the first signer's address (Alice). 31 | let tx = TransactionRequest::default() 32 | .with_to(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045")) 33 | .with_value(U256::from(100)); 34 | 35 | // Send the transaction and wait for inclusion. 36 | let tx_hash = provider.send_transaction(tx).await?.watch().await?; 37 | 38 | println!("Sent transaction: {tx_hash}"); 39 | 40 | Ok(()) 41 | } 42 | -------------------------------------------------------------------------------- /examples/wallets/examples/sign_message.rs: -------------------------------------------------------------------------------- 1 | //! Example of signing a message with a signer. 2 | 3 | use alloy::signers::{local::PrivateKeySigner, Signer}; 4 | use eyre::Result; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<()> { 8 | // Set up a random signer. 9 | let signer = PrivateKeySigner::random(); 10 | 11 | // Optionally, the wallet's chain id can be set, in order to use EIP-155 12 | // replay protection with different chains. 13 | let signer = signer.with_chain_id(Some(1337)); 14 | 15 | // The message to sign. 16 | let message = b"hello"; 17 | 18 | // Sign the message asynchronously with the signer. 19 | let signature = signer.sign_message(message).await?; 20 | 21 | println!("Signature produced by {}: {:?}", signer.address(), signature); 22 | println!("Signature recovered address: {}", signature.recover_address_from_msg(&message[..])?); 23 | 24 | Ok(()) 25 | } 26 | -------------------------------------------------------------------------------- /examples/wallets/examples/sign_permit_hash.rs: -------------------------------------------------------------------------------- 1 | //! Example of signing a permit hash using a wallet. 2 | 3 | use alloy::{ 4 | primitives::{address, keccak256, U256}, 5 | signers::{local::PrivateKeySigner, Signer}, 6 | sol, 7 | sol_types::{eip712_domain, SolStruct}, 8 | }; 9 | use eyre::Result; 10 | use serde::Serialize; 11 | 12 | sol! { 13 | #[allow(missing_docs)] 14 | #[derive(Serialize)] 15 | struct Permit { 16 | address owner; 17 | address spender; 18 | uint256 value; 19 | uint256 nonce; 20 | uint256 deadline; 21 | } 22 | } 23 | 24 | #[tokio::main] 25 | async fn main() -> Result<()> { 26 | // Set up a random signer. 27 | let signer = PrivateKeySigner::random(); 28 | 29 | let domain = eip712_domain! { 30 | name: "Uniswap V2", 31 | version: "1", 32 | chain_id: 1, 33 | verifying_contract: address!("B4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc"), 34 | salt: keccak256("test"), 35 | }; 36 | 37 | let permit = Permit { 38 | owner: signer.address(), 39 | spender: address!("B4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc"), 40 | value: U256::from(100), 41 | nonce: U256::from(0), 42 | deadline: U256::from(0), 43 | }; 44 | 45 | // Derive the EIP-712 signing hash. 46 | let hash = permit.eip712_signing_hash(&domain); 47 | 48 | // Sign the hash asynchronously with the wallet. 49 | let signature = signer.sign_hash(&hash).await?; 50 | 51 | println!( 52 | "Recovered address matches wallet address: {}", 53 | signature.recover_address_from_prehash(&hash)? == signer.address() 54 | ); 55 | 56 | println!("Wallet signature matches: {}", signer.sign_hash(&hash).await? == signature); 57 | 58 | Ok(()) 59 | } 60 | -------------------------------------------------------------------------------- /examples/wallets/examples/trezor_signer.rs: -------------------------------------------------------------------------------- 1 | //! Example of signing and sending a transaction using a Trezor device. 2 | 3 | use alloy::{ 4 | network::TransactionBuilder, 5 | primitives::{address, U256}, 6 | providers::{Provider, ProviderBuilder}, 7 | rpc::types::TransactionRequest, 8 | signers::trezor::{HDPath, TrezorSigner}, 9 | }; 10 | use eyre::Result; 11 | 12 | #[tokio::main] 13 | async fn main() -> Result<()> { 14 | // Instantiate the application by acquiring a lock on the Trezor device. 15 | let signer = TrezorSigner::new(HDPath::TrezorLive(0), Some(1)).await?; 16 | 17 | // Create a provider with the wallet. 18 | let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc".parse()?; 19 | let provider = ProviderBuilder::new().wallet(signer).connect_http(rpc_url); 20 | 21 | // Build a transaction to send 100 wei from Alice to Vitalik. 22 | // The `from` field is automatically filled to the first signer's address (Alice). 23 | let vitalik = address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"); 24 | let tx = TransactionRequest::default().with_to(vitalik).with_value(U256::from(100)); 25 | 26 | // Send the transaction and wait for inclusion with 3 confirmations. 27 | let tx_hash = 28 | provider.send_transaction(tx).await?.with_required_confirmations(3).watch().await?; 29 | 30 | println!("Sent transaction: {tx_hash}"); 31 | 32 | Ok(()) 33 | } 34 | -------------------------------------------------------------------------------- /examples/wallets/examples/verify_message.rs: -------------------------------------------------------------------------------- 1 | //! Example of verifying that a message was signed by the provided address. 2 | 3 | use alloy::signers::{local::PrivateKeySigner, SignerSync}; 4 | use eyre::Result; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<()> { 8 | // Instantiate a signer. 9 | let signer = PrivateKeySigner::random(); 10 | 11 | // Sign a message. 12 | let message = "Some data"; 13 | let signature = signer.sign_message_sync(message.as_bytes())?; 14 | 15 | // Recover the signer from the message. 16 | let recovered = signature.recover_address_from_msg(message)?; 17 | assert_eq!(recovered, signer.address()); 18 | 19 | Ok(()) 20 | } 21 | -------------------------------------------------------------------------------- /examples/wallets/examples/yubi_signer.rs: -------------------------------------------------------------------------------- 1 | //! Example of signing and sending a transaction using a Yubi device. 2 | 3 | use alloy::{ 4 | network::TransactionBuilder, 5 | primitives::{address, U256}, 6 | providers::{Provider, ProviderBuilder}, 7 | rpc::types::TransactionRequest, 8 | signers::local::{ 9 | yubihsm::{Connector, Credentials, UsbConfig}, 10 | YubiSigner, 11 | }, 12 | }; 13 | use eyre::Result; 14 | 15 | #[tokio::main] 16 | async fn main() -> Result<()> { 17 | // We use USB for the example, but you can connect over HTTP as well. Refer 18 | // to the [YubiHSM](https://docs.rs/yubihsm/0.34.0/yubihsm/) docs for more information. 19 | let connector = Connector::usb(&UsbConfig::default()); 20 | 21 | // Instantiate the connection to the YubiKey. Alternatively, use the 22 | // `from_key` method to upload a key you already have, or the `new` method 23 | // to generate a new keypair. 24 | let signer = YubiSigner::connect(connector, Credentials::default(), 0); 25 | 26 | // Create a provider with the wallet. 27 | let rpc_url = "https://reth-ethereum.ithaca.xyz/rpc".parse()?; 28 | let provider = ProviderBuilder::new().wallet(signer).connect_http(rpc_url); 29 | 30 | // Build a transaction to send 100 wei from Alice to Vitalik. 31 | // The `from` field is automatically filled to the first signer's address (Alice). 32 | let vitalik = address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"); 33 | let tx = TransactionRequest::default().with_to(vitalik).with_value(U256::from(100)); 34 | 35 | // Send the transaction and wait for inclusion with 3 confirmations. 36 | let tx_hash = 37 | provider.send_transaction(tx).await?.with_required_confirmations(3).watch().await?; 38 | 39 | println!("Sent transaction: {tx_hash}"); 40 | 41 | Ok(()) 42 | } 43 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_imports = true 2 | use_field_init_shorthand = true 3 | use_small_heuristics = "Max" 4 | 5 | # Nightly 6 | max_width = 100 7 | comment_width = 100 8 | imports_granularity = "Crate" 9 | wrap_comments = true 10 | format_code_in_doc_comments = true 11 | doc_comment_code_block_width = 100 12 | format_macro_matchers = true 13 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Exit if anything fails. 4 | set -eo pipefail 5 | 6 | # Utilities 7 | GREEN="\033[00;32m" 8 | 9 | function log () { 10 | echo -e "$1" 11 | echo "################################################################################" 12 | echo "#### $2 " 13 | echo "################################################################################" 14 | echo -e "\033[0m" 15 | } 16 | 17 | # This script will do the following: 18 | # 19 | # 1. Gather all the examples from the output of `cargo run --example` command. 20 | # 2. Filter out the examples that have external dependencies or are not meant to be run. 21 | # 3. Pre-build the filtered examples prior to running them. 22 | # 4. Run all the examples in parallel (up to 10) that are left after filtering. 23 | function main () { 24 | export examples="$( 25 | cargo run --example 2>&1 \ 26 | | grep -E '^ ' \ 27 | | grep -v \ 28 | -e 'any_network' \ 29 | -e 'aws_signer' \ 30 | -e 'builtin' \ 31 | -e 'debug_trace_call_many' \ 32 | -e 'ethereum_wallet' \ 33 | -e 'foundry_fork_db' \ 34 | -e 'gcp_signer' \ 35 | -e 'geth_local_instance' \ 36 | -e 'ipc' \ 37 | -e 'keystore_signer' \ 38 | -e 'ledger_signer' \ 39 | -e 'permit2_signature_transfer' \ 40 | -e 'reth_db_layer' \ 41 | -e 'reth_db_layer' \ 42 | -e 'reth_db_provider' \ 43 | -e 'reth_db_provider' \ 44 | -e 'reth_local_instance' \ 45 | -e 'subscribe_all_logs' \ 46 | -e 'subscribe_logs' \ 47 | -e 'subscribe_pending_transactions' \ 48 | -e 'trace_call_many' \ 49 | -e 'trace_call' \ 50 | -e 'trace_transaction' \ 51 | -e 'trezor_signer' \ 52 | -e 'ws_auth' \ 53 | -e 'ws' \ 54 | -e 'yubi_signer' \ 55 | | xargs -n1 echo 56 | )" 57 | 58 | log $GREEN "Building..." 59 | 60 | # Pre-build the filtered examples prior to running them. 61 | cargo build $(printf -- '--example %s ' $examples) 62 | 63 | log $GREEN "Running..." 64 | 65 | # Run all the examples that are left after filtering. 66 | printf '%s\n' $examples \ 67 | | xargs -P4 -I{} bash -c ' 68 | bin="./target/debug/examples/{}" 69 | if [[ -x "$bin" ]]; then 70 | "$bin" >/dev/null \ 71 | && echo "Successfully ran: {}" \ 72 | || { echo "Failed to run: {}" >&2; exit 1; } 73 | else 74 | echo "Missing binary: $bin" >&2 75 | exit 1 76 | fi 77 | ' 78 | 79 | log $GREEN "Done" 80 | } 81 | 82 | # Run the main function. 83 | # This prevents partial execution in case of incomplete downloads. 84 | main --------------------------------------------------------------------------------