Merge branch 'master' into feat/syn-2

This commit is contained in:
DaniPopes 2023-03-25 01:48:29 +01:00
commit 0ff8d6942c
No known key found for this signature in database
GPG Key ID: 0F09640DDB7AC692
80 changed files with 3466 additions and 1068 deletions

View File

@ -29,5 +29,4 @@ the code change.
- [ ] Added Tests - [ ] Added Tests
- [ ] Added Documentation - [ ] Added Documentation
- [ ] Updated the changelog
- [ ] Breaking changes - [ ] Breaking changes

View File

@ -63,11 +63,11 @@ jobs:
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- name: test ${{ matrix.flags.flags }} - name: test ${{ matrix.flags.flags }}
shell: bash shell: bash
# skip `ethers_etherscan::it` and `ethers::live` # skip `ethers_etherscan::it`
run: | run: |
cargo nextest run \ cargo nextest run \
${{ matrix.flags.flags }} \ ${{ matrix.flags.flags }} \
-E "!binary(~live) & !(deps(ethers-etherscan) & kind(test))" -E "!(deps(ethers-etherscan) & kind(test))"
etherscan-tests: etherscan-tests:
name: etherscan tests name: etherscan tests
@ -94,7 +94,7 @@ jobs:
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- name: live tests - name: live tests
run: cargo test -p ethers --test live --all-features run: cargo test -p ethers --all-features
# TODO: [#2191](https://github.com/gakonst/ethers-rs/issues/2191) # TODO: [#2191](https://github.com/gakonst/ethers-rs/issues/2191)
# feature-checks: # feature-checks:
@ -172,7 +172,7 @@ jobs:
with: with:
node-version: 16 node-version: 16
- name: Run wasm example - name: Run wasm example
working-directory: examples/ethers-wasm working-directory: examples/wasm
run: | run: |
yarn yarn
yarn anvil & yarn anvil &
@ -194,4 +194,4 @@ jobs:
run: ./.github/scripts/install_test_binaries.sh run: ./.github/scripts/install_test_binaries.sh
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- name: Build and run all examples - name: Build and run all examples
run: ./scripts/examples.sh run: ./bin/run_all_examples

View File

@ -1,99 +0,0 @@
name: release
on:
schedule:
- cron: "0 0 * * 0"
workflow_dispatch:
inputs:
release_type:
type: choice
description: Release type
options:
- major
- minor
- patch
- rc
- beta
- alpha
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
env:
CARGO_TOKEN: ${{ secrets.CARGO_TOKEN }}
RELEASE_TYPE: ${{ github.event.inputs.release_type }}
steps:
- name: Checkout sources
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Configure git
run: |
git config user.name github-actions
git config user.email github-actions@github.com
- name: Rust stable
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
- uses: Swatinem/rust-cache@v1
with:
cache-on-failure: true
- name: Install cargo-release
uses: actions-rs/install@v0.1
with:
crate: cargo-release
version: latest
- name: Cargo login
run: |
cargo login $CARGO_TOKEN
- name: Dry-run cargo release
run: |
cargo release --workspace ${RELEASE_TYPE:-alpha} --exclude ethers-wasm
- name: Publish release
run: |
cargo release --workspace ${RELEASE_TYPE:-alpha} --exclude ethers-wasm --execute --no-confirm
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: 14
- run: |
npm i semver
- name: Install git-cliff
uses: actions-rs/install@v0.1
with:
crate: git-cliff
version: latest
- name: Publish changelog
id: changelog
run: |
current_version=$(git tag --contains HEAD -l "v*" | head -1)
from_version=$(node .github/scripts/release-tag-from.js $current_version $RELEASE_TYPE)
echo from $from_version to $current_version
echo "::set-output name=release_version::$(echo $current_version)"
if git rev-parse "$from_version" >/dev/null 2>&1; then
echo "tag exists, can generate changelog";
else
echo "tag does not exist, cannot generate changelog, publish github release manually"
exit 0
fi
git cliff $from_version..$current_version > GENERATED_CHANGELOG.md
cat GENERATED_CHANGELOG.md
- name: Create GitHub release
id: release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_VERSION: ${{ steps.changelog.outputs.release_version }}
with:
tag_name: ${{ env.RELEASE_VERSION }}
release_name: ${{ env.RELEASE_VERSION }}
body_path: GENERATED_CHANGELOG.md
prerelease: ${{ env.RELEASE_TYPE == 'alpha' }}

406
CHANGELOG-OLD.md Normal file
View File

@ -0,0 +1,406 @@
# Changelog
The old `ethers-rs` changelog, that has been discontinued.
The new one is located [here](./CHANGELOG.md), which is automatically generated by [git-cliff](https://github.com/orhun/git-cliff).
## ethers-core
### Unreleased
- Add support for `ethlive` as a chain name [#2268](https://github.com/gakonst/ethers-rs/pull/2268)
- Make `Chain` more round-trip friendly [#2270](https://github.com/gakonst/ethers-rs/pull/2270)
- Add `other: OtherFields` to `TransactionReceipt` [#2209](https://github.com/gakonst/ethers-rs/pull/2209)
- Add `Signature::recover_typed_data` [#2120](https://github.com/gakonst/ethers-rs/pull/2120)
- Add `abi::encode_packed` [#2104](https://github.com/gakonst/ethers-rs/pull/2104)
- Add support for custom JavaScript tracer to `debug_traceCall` and `debug_traceTransaction` [#2064](https://github.com/gakonst/ethers-rs/pull/2064)
- Add a `Send` bound to the `IntoFuture` implementation of `ContractCall` [#2083](https://github.com/gakonst/ethers-rs/pull/2083)
- Bump [`svm-rs`](https://github.com/roynalnaruto/svm-rs) dependency to fix conflicts with Rust Crytpo packages [#2051](https://github.com/gakonst/ethers-rs/pull/2051)
- Avoid unnecessary allocations in `utils` [#2046](https://github.com/gakonst/ethers-rs/pull/2046)
- Add abigen support for hardhat generated bytecode json format [#2012](https://github.com/gakonst/ethers-rs/pull/2012)
- Fix typo in `RwClient` docs for `write_client` method.
- Add support for Geth `debug_traceCall` [#1949](https://github.com/gakonst/ethers-rs/pull/1949)
- Add support for Geth built-in tracer and config [#2121](https://github.com/gakonst/ethers-rs/pull/2121)
- Graceful handling of WebSocket transport errors [#1889](https://github.com/gakonst/ethers-rs/issues/1889) [#1815](https://github.com/gakonst/ethers-rs/issues/1815)
- `MiddlewareBuilder` trait to instantiate a `Provider` as `Middleware` layers.
- An `Event` builder can be instantiated specifying the event filter type, without the need to instantiate a contract.
- Add 'ethers_core::types::OpCode' and use in 'ethers_core::types::VMOperation' [#1857](https://github.com/gakonst/ethers-rs/issues/1857)
- Remove rust_decimals dependency for ethers-core
- Add support for numbers greater than 2^96 for `ethers_core::utils::parse_units` [#1822](https://github.com/gakonst/ethers-rs/issues/1822)
- Add comment about safety of u8 -> u64 cast in `ethers_core::types::Signature`
- Stop defaulting to the `"latest"` block in `eth_estimateGas` params [#1657](https://github.com/gakonst/ethers-rs/pull/1657)
- Fix geth trace types for debug_traceTransaction rpc
- Fix RLP decoding of legacy `Transaction`
- Fix RLP encoding of `TransactionReceipt` [#1661](https://github.com/gakonst/ethers-rs/pull/1661)
- Add `Unit8` helper type [#1639](https://github.com/gakonst/ethers-rs/pull/1639)
- Add `evm.deployedBytecode.immutableReferences` output selector [#1523](https://github.com/gakonst/ethers-rs/pull/1523)
- Added `get_erc1155_token_transfer_events` function for etherscan client [#1503](https://github.com/gakonst/ethers-rs/pull/1503)
- Add support for Geth `debug_traceTransaction` [#1469](https://github.com/gakonst/ethers-rs/pull/1469)
- Use correct, new transaction type for `typool_content` RPC endpoint [#1501](https://github.com/gakonst/ethers-rs/pull/1501)
- Fix the default config for generated `BuildInfo` [#1458](https://github.com/gakonst/ethers-rs/pull/1458)
- Allow configuration of the output directory of the generated `BuildInfo` [#1433](https://github.com/gakonst/ethers-rs/pull/1433)
- capture unknown fields in `Block` and `Transaction` type via new `OtherFields` type [#1423](https://github.com/gakonst/ethers-rs/pull/1423)
- Methods like `set_to()` from `TypedTransaction` can be chained
- Use H64 for Block Nonce [#1396](https://github.com/gakonst/ethers-rs/pull/1396)
- Add `as_*_mut` methods on `TypedTransaction`
[#1310](https://github.com/gakonst/ethers-rs/pull/1310)
- AWS EIP712 data signing no longer signs with EIP155
- Added Cronos testnet to etherscan options [#1276](https://github.com/gakonst/ethers-rs/pull/1276)
- Fix parsing of a pending block
[#1272](https://github.com/gakonst/ethers-rs/pull/1272)
- Removed Cronos mainnet beta from `is_legacy` [1246](https://github.com/gakonst/ethers-rs/pull/1246)
- Fix RLP decoding of `from` field for `Eip1559TransactionRequest` and
`Eip2930TransactionRequest`, remove `Eip1559TransactionRequest` `sighash`
method [#1180](https://github.com/gakonst/ethers-rs/pull/1180)
- Fix RLP encoding of absent access list in `Transaction` [1137](https://github.com/gakonst/ethers-rs/pull/1137)
- Pass compilation time as additional argument to `Reporter::on_solc_success` [#1098](https://github.com/gakonst/ethers-rs/pull/1098)
- Fix aws signer bug which maps un-normalized signature to error if no normalization occurs (in `aws::utils::decode_signature`)
- Implement signed transaction RLP decoding [#1096](https://github.com/gakonst/ethers-rs/pull/1096)
- `Transaction::from` will default to `Address::zero()`. Add `recover_from` and
`recover_from_mut` methods for recovering the sender from signature, and also
setting the same on tx [#1075](https://github.com/gakonst/ethers-rs/pull/1075).
- Add Etherscan account API endpoints [#939](https://github.com/gakonst/ethers-rs/pull/939)
- Add FTM Mainet and testnet to parse method "try_from" from Chain.rs and add cronos mainet and testnet to "from_str"
- Add FTM mainnet and testnet Multicall addresses [#927](https://github.com/gakonst/ethers-rs/pull/927)
- Add Cronos mainnet beta and testnet to the list of known chains
[#926](https://github.com/gakonst/ethers-rs/pull/926)
- `Chain::to_string` will return the same chain name as `Chain::from_str`
- Add `eth_syncing` [#848](https://github.com/gakonst/ethers-rs/pull/848)
- Fix overflow and possible divide-by-zero in `estimate_priority_fee`
- Add BSC mainnet and testnet to the list of known chains
[#831](https://github.com/gakonst/ethers-rs/pull/831)
- Returns error on invalid type conversion instead of panicking
[#691](https://github.com/gakonst/ethers-rs/pull/691/files)
- Change types mapping for solidity `bytes` to rust `ethers::core::Bytes` and
solidity `uint8[]` to rust `Vec<u8>`.
[#613](https://github.com/gakonst/ethers-rs/pull/613)
- Fix `format_units` to return a `String` of representing a decimal point float
such that the decimal places don't get truncated.
[#597](https://github.com/gakonst/ethers-rs/pull/597)
- Implement hex display format for `ethers::core::Bytes`
[#624](https://github.com/gakonst/ethers-rs/pull/624).
- Fix `fee_history` to first try with `block_count` encoded as a hex `QUANTITY`.
[#668](https://github.com/gakonst/ethers-rs/pull/668)
- Fix `fill_transaction` to set nonces in transactions, if the sender is known
and no nonce is specified
- Move `fill_transaction` implementation to the provider, to allow middleware
to properly override its behavior.
- Add informational messages to solc installation and compilation.
- Significantly refactor `MultiAbigen` module generation. Now allows for lib
generation, and does not make unnecessary disk writes.
[#854](https://github.com/gakonst/ethers-rs/pull/852)
- Refactor `ethers-contract-abigen` to use `eyre` instead of `anyhow` via
[#858](https://github.com/gakonst/ethers-rs/pull/858)
- Add `Deployer.send_with_receipt -> Result<(Contract, Receipt), Error>`
so that the receipt can be returned to the called when deploying
a contract [#865](https://github.com/gakonst/ethers-rs/pull/865)
- Add Arbitrum mainnet and testnet to the list of known chains
- Add ENS avatar and TXT records resolution
[#889](https://github.com/gakonst/ethers-rs/pull/889)
- Do not override gas limits provided by an outer middleware when including an EIP-2930 access list
[#901](https://github.com/gakonst/ethers-rs/pull/901)
- Add a getter to `ProjectCompileOutput` that returns a mapping of compiler
versions to a vector of name + contract struct tuples
[#908](https://github.com/gakonst/ethers-rs/pull/908)
- Add Yul compilation [#994](https://github.com/gakonst/ethers-rs/pull/994)
- Enforce commutativity of ENS reverse resolution
[#996](https://github.com/gakonst/ethers-rs/pull/996)
- Add `TransactionReceipt::to` and `TransactionReceipt::from`
[#1184](https://github.com/gakonst/ethers-rs/pull/1184)
- Add `From<H160>` and From<Vec<H160>> traits to `ValueOrArray<H160>` [#1199](https://github.com/gakonst/ethers-rs/pull/1200)
- Fix handling of Websocket connection errors [#1287](https://github.com/gakonst/ethers-rs/pull/1287)
- Add Arithmetic Shift Right operation for I256 [#1323](https://github.com/gakonst/ethers-rs/issues/1323)
- [#1535](https://github.com/gakonst/ethers-rs/pull/1535) Add support to Aurora and Aurora testnet networks.
- [#1632](https://github.com/gakonst/ethers-rs/pull/1632) Re-export `H32` from `ethabi`.
- [#1634](https://github.com/gakonst/ethers-rs/pull/1634) Derive missing `Clone`, `Copy` and `Debug` impls in ethers-etherscan.
- Bytes debug format now displays hex literals [#1658](https://github.com/gakonst/ethers-rs/pull/1658)
- [#1451](https://github.com/gakonst/ethers-rs/issues/1451) Add Arithmetic Shift Left operation for I256
- [#1860](https://github.com/gakonst/ethers-rs/pull/1860) Update I256 type documentation calling out the inconsistency
between its right shift operator and standard library numeric types.
- [#842](https://github.com/gakonst/ethers-rs/issues/842) Add support for I256 types in `parse_units` and `format_units`.
Added `twos_complement` function for I256.
- [#1934](https://github.com/gakonst/ethers-rs/pull/1934) Allow 16 calls in multicall.
- [#1941](https://github.com/gakonst/ethers-rs/pull/1941) Add `add_calls` and `call_array` for `Multicall`.
- Added basic event log filtering example.
## ethers-contract-abigen
### Unreleased
- Abigen now generates events with new `<B, M>` generic pattern [#2103](https://github.com/gakonst/ethers-rs/pull/2103)
- Fix Cargo.toml generation issue that could cause dependency conflicts [#1852](https://github.com/gakonst/ethers-rs/pull/1852)
- Use corresponding rust structs for event fields if they're solidity structs [#1674](https://github.com/gakonst/ethers-rs/pull/1674)
- Add `ContractFilter` to filter contracts in `MultiAbigen` [#1564](https://github.com/gakonst/ethers-rs/pull/1564)
- generate error bindings for custom errors [#1549](https://github.com/gakonst/ethers-rs/pull/1549)
- Support overloaded events
[#1233](https://github.com/gakonst/ethers-rs/pull/1233)
- Relax Clone requirements when Arc<Middleware> is used
[#1183](https://github.com/gakonst/ethers-rs/pull/1183)
- Generate a deploy function if bytecode is provided in the abigen! input (json artifact)
[#1030](https://github.com/gakonst/ethers-rs/pull/1030).
- Generate correct bindings of struct's field names that are reserved words
[#989](https://github.com/gakonst/ethers-rs/pull/989).
- Generate correct binding module names that are reserved words
[#1498](https://github.com/gakonst/ethers-rs/pull/1498). Note: this changes
generated module names to snake case. For example, `MyContract` is now
`my_contract` rather than `mycontract_mod`.
- The `Cargo.toml` generated by bindings now includes the `abigen` feature on
ethers. [#1508](https://github.com/gakonst/ethers-rs/pull/1508)
- More descriptive contract deserialization errors.
[#1633](https://github.com/gakonst/ethers-rs/pull/1633)
### 0.6.0
- Add `MultiAbigen` to generate a series of contract bindings that can be kept in the repo
[#724](https://github.com/gakonst/ethers-rs/pull/724).
- Add provided `event_derives` to call and event enums as well
[#721](https://github.com/gakonst/ethers-rs/pull/721).
- Implement snowtrace and polygonscan on par with the etherscan integration
[#666](https://github.com/gakonst/ethers-rs/pull/666).
## ethers-solc
### Unreleased
- Add `OutputContext` to `ArtifactOutput` trait
[#1621](https://github.com/gakonst/ethers-rs/pull/1621)
- On windows all paths in the `ProjectCompilerOutput` are now slashed by default
[#1540](https://github.com/gakonst/ethers-rs/pull/1540)
- `ArtifactOutput::write_extras` now takes the `Artifacts` directly
[#1491](https://github.com/gakonst/ethers-rs/pull/1491)
- Make `ethers-solc` optional dependency of `ethers`, needs `ethers-solc` feature to activate
[#1463](https://github.com/gakonst/ethers-rs/pull/1463)
- Add `rawMetadata:String` field to configurable contract output
[#1365](https://github.com/gakonst/ethers-rs/pull/1365)
- Use relative source paths and `solc --base-path`
[#1317](https://github.com/gakonst/ethers-rs/pull/1317)
- Save cache entry objects with relative paths
[#1307](https://github.com/gakonst/ethers-rs/pull/1307)
- Bundle svm, svm-builds and sha2 dependencies in new `svm-solc` feature
[#1071](https://github.com/gakonst/ethers-rs/pull/1071)
- Emit artifact files for source files without any ContractDefinition
[#1296](https://github.com/gakonst/ethers-rs/pull/1296)
- Wrap `ethabi::Contract` into new type `LosslessAbi` and `abi: Option<Abi>` with `abi: Option<LosslessAbi>` in `ConfigurableContractArtifact`
[#952](https://github.com/gakonst/ethers-rs/pull/952)
- Let `Project` take ownership of `ArtifactOutput` and change trait interface
[#907](https://github.com/gakonst/ethers-rs/pull/907)
- Total revamp of the `Project::compile` pipeline
[#802](https://github.com/gakonst/ethers-rs/pull/802)
- Support multiple versions of compiled contracts
- Breaking: deprecate hardhat cache file compatibility, cache file now tracks artifact paths and their versions
- Fix flatten replacement target location
[#846](https://github.com/gakonst/ethers-rs/pull/846)
- Fix duplicate files during flattening
[#813](https://github.com/gakonst/ethers-rs/pull/813)
- Add ability to flatten file imports
[#774](https://github.com/gakonst/ethers-rs/pull/774)
- Add dependency graph and resolve all imported libraryfiles
[#750](https://github.com/gakonst/ethers-rs/pull/750)
- `Remapping::find_many` does not return a `Result` anymore
[#707](https://github.com/gakonst/ethers-rs/pull/707)
- Add support for hardhat artifacts
[#677](https://github.com/gakonst/ethers-rs/pull/677)
- Add more utility functions to the `Artifact` trait
[#673](https://github.com/gakonst/ethers-rs/pull/673)
- Return cached artifacts from project `compile` when the cache only contains
some files
- Add support for library linking and make `Bytecode`'s `object` filed an
`enum BytecodeObject` [#656](https://github.com/gakonst/ethers-rs/pull/656).
- Nit: remove accidentally doubled double-quotes in an error message
- Fix when compiler-out metadata is empty and there's no internalType [#1182](https://github.com/gakonst/ethers-rs/pull/1182)
- Add basic `solc` model checker options.
[#1258](https://github.com/gakonst/ethers-rs/pull/1258)
### 0.6.0
- add `EthAbiCodec` proc macro to derive `AbiEncode` `AbiDecode` implementation
[#704](https://github.com/gakonst/ethers-rs/pull/704)
- move `AbiEncode` `AbiDecode` trait to ethers-core and implement for core types
[#531](https://github.com/gakonst/ethers-rs/pull/531)
- Add EIP-712 `sign_typed_data` signer method; add ethers-core type `Eip712`
trait and derive macro in ethers-derive-eip712
[#481](https://github.com/gakonst/ethers-rs/pull/481)
### 0.5.3
- Allow configuring the optimizer & passing arbitrary arguments to solc
[#427](https://github.com/gakonst/ethers-rs/pull/427)
- Decimal support for `ethers_core::utils::parse_units`
[#463](https://github.com/gakonst/ethers-rs/pull/463)
- Fixed Wei unit calculation in `Units`
[#460](https://github.com/gakonst/ethers-rs/pull/460)
- Add `ethers_core::utils::get_create2_address_from_hash`
[#444](https://github.com/gakonst/ethers-rs/pull/444)
- Bumped ethabi to 0.15.0 and fixing breaking changes
[#469](https://github.com/gakonst/ethers-rs/pull/469),
[#448](https://github.com/gakonst/ethers-rs/pull/448),
[#445](https://github.com/gakonst/ethers-rs/pull/445)
### 0.5.2
- Correctly RLP Encode transactions as received from the mempool
([#415](https://github.com/gakonst/ethers-rs/pull/415))
## ethers-providers
### Unreleased
- Breaking: WS now includes reconnection logic and a changed `connect`
interface. Old behavior can be accessed via the `legacy_ws` feature
[#2181](https://github.com/gakonst/ethers-rs/pull/2181)
- Re-organize the crate. #[2150](https://github.com/gakonst/ethers-rs/pull/2159)
- Convert provider errors to arbitrary middleware errors
[#1920](https://github.com/gakonst/ethers-rs/pull/1920)
- Add a subset of the `admin` namespace
[1880](https://github.com/gakonst/ethers-rs/pull/1880)
- Return String for net version
[1376](https://github.com/gakonst/ethers-rs/pull/1376)
- Stream of paginated logs that load logs in small pages
[1285](https://github.com/gakonst/ethers-rs/pull/1285)
- Load previous logs before subscribing to new logs in case fromBlock is set
[1264](https://github.com/gakonst/ethers-rs/pull/1264)
- Add retries to the pending transaction future
[1221](https://github.com/gakonst/ethers-rs/pull/1221)
- Add support for basic and bearer authentication in http and non-wasm websockets.
[829](https://github.com/gakonst/ethers-rs/pull/829)
- Export `ethers_providers::IpcError` and `ethers_providers::QuorumError`
[1012](https://github.com/gakonst/ethers-rs/pull/1012)
### 0.6.0
- re-export error types for `Http` and `Ws` providers in
[#570](https://github.com/gakonst/ethers-rs/pull/570)
- add a method on the `Middleware` to broadcast a tx with a series of escalating
gas prices via [#566](https://github.com/gakonst/ethers-rs/pull/566)
- Remove unnecessary `Serialize` constraint to `R` (the Response type) in the
`request` method of `JsonRpcClient`.
- Fix `http Provider` data race when generating new request `id`s.
- Add support for `net_version` RPC method.
[595](https://github.com/gakonst/ethers-rs/pull/595)
- Add support for `evm_snapshot` and `evm_revert` dev RPC methods.
[640](https://github.com/gakonst/ethers-rs/pull/640)
### 0.5.3
- Expose `ens` module [#435](https://github.com/gakonst/ethers-rs/pull/435)
- Add `eth_getProof` [#459](https://github.com/gakonst/ethers-rs/pull/459)
### 0.5.2
- Set resolved ENS name during gas estimation
([1e5a9e](https://github.com/gakonst/ethers-rs/commit/1e5a9efb3c678eecd43d5c341b4932da35445831))
## ethers-signers
### Unreleased
- fix: `LedgerSigner` has improved tracing and a ledger app bug mitigation
[#2192](https://github.com/gakonst/ethers-rs/pull/2192)
- `eth-keystore-rs` crate updated. Allow an optional name for the to-be-generated
keystore file [#910](https://github.com/gakonst/ethers-rs/pull/910)
- [1983](https://github.com/gakonst/ethers-rs/pull/1983) Added a `from_bytes` function for the `Wallet` type.
- Allow parsing of private key that has `0x` prefix
[#2037](https://github.com/gakonst/ethers-rs/pull/2037)
### 0.6.0
- `LocalWallet::new_keystore` now returns a tuple `(LocalWallet, String)`
instead of `LocalWallet`, where the string represents the UUID of the newly
created encrypted JSON keystore. The JSON keystore is stored as a file
`/dir/uuid`. The issue [#557](https://github.com/gakonst/ethers-rs/issues/557)
is addressed [#559](https://github.com/gakonst/ethers-rs/pull/559)
## ethers-contract
### Unreleased
- (Breaking) Add `Revert` to `ContractError`. Add `impl EthError for String`.
Modify existing `ContractError` variants to prevent accidental improper
usage. Change `MulticallError` to use `ContractError::Revert`. Add
convenience methods to decode errors from reverts.
[#2172](https://github.com/gakonst/ethers-rs/pull/2172)
- (Breaking) Improve Multicall result handling
[#2164](https://github.com/gakonst/ethers-rs/pull/2105)
- (Breaking) Make `Event` objects generic over borrow & remove lifetime
[#2105](https://github.com/gakonst/ethers-rs/pull/2105)
- Make `Factory` objects generic over the borrow trait, to allow non-arc mware
[#2103](https://github.com/gakonst/ethers-rs/pull/2103)
- Make `Contract` objects generic over the borrow trait, to allow non-arc mware
[#2082](https://github.com/gakonst/ethers-rs/pull/2082)
- Return pending transaction from `Multicall::send`
[#2044](https://github.com/gakonst/ethers-rs/pull/2044)
- Add abigen to default features
[#1684](https://github.com/gakonst/ethers-rs/pull/1684)
- Add extra Multicall helper methods
[#1666](https://github.com/gakonst/ethers-rs/pull/1666)
- Update Multicall to Multicall3
[#1584](https://github.com/gakonst/ethers-rs/pull/1584)
- Add `Event::stream_with_meta` and `Event::subscribe_with_meta`
[#1483](https://github.com/gakonst/ethers-rs/pull/1483)
- Added tx builder methods to `ContractFactory`
[#1289](https://github.com/gakonst/ethers-rs/pull/1289)
- Relax Clone requirements when Arc<Middleware> is used
[#1183](https://github.com/gakonst/ethers-rs/pull/1183)
- Add `EventStream::select` to combine streams with different event types
[#725](https://github.com/gakonst/ethers-rs/pull/725)
- Substitute output tuples with rust struct types for function calls
[#664](https://github.com/gakonst/ethers-rs/pull/664)
- Add AbiType implementation during EthAbiType expansion
[#647](https://github.com/gakonst/ethers-rs/pull/647)
- fix Etherscan conditional HTTP support
[#632](https://github.com/gakonst/ethers-rs/pull/632)
- use `CARGO_MANIFEST_DIR` as root for relative paths in abigen
[#631](https://github.com/gakonst/ethers-rs/pull/631)
### 0.6.0
- Provide a way to opt out of networking support in abigen proc macro with
`abigen-offline` feature [#580](https://github.com/gakonst/ethers-rs/pull/580)
- Add `.call()` method to `Deployer` for performing dry runs of contract
deployments. [#554](https://github.com/gakonst/ethers-rs/pull/554)
- Improve error message from failure in `ethers_contract_abigen::Source::parse`
[#552](https://github.com/gakonst/ethers-rs/pull/552)
- use enumerated aliases for overloaded functions
[#545](https://github.com/gakonst/ethers-rs/pull/545)
- add `EthCall` trait and derive macro which generates matching structs for
contract calls [#517](https://github.com/gakonst/ethers-rs/pull/517)
- Use rust types as contract function inputs for human readable abi
[#482](https://github.com/gakonst/ethers-rs/pull/482)
- `abigen!` now generates `Display` for all events using the new `EthDisplay`
macro [#513](https://github.com/gakonst/ethers-rs/pull/513)
- `abigen!` now supports overloaded functions natively
[#501](https://github.com/gakonst/ethers-rs/pull/501)
- `abigen!` now supports multiple contracts
[#498](https://github.com/gakonst/ethers-rs/pull/498)
### Unreleased
### 0.5.3
- (De)Tokenize structs and events with only a single field as `Token:Tuple`
([#417](https://github.com/gakonst/ethers-rs/pull/417))
## ethers-middleware
### Unreleased
- Added `openssl` and `rustls` feature flags
[#1961](https://github.com/gakonst/ethers-rs/pull/1961)
- Relax Clone requirements when Arc<Middleware> is used
[#1183](https://github.com/gakonst/ethers-rs/pull/1183)
- Ensure a consistent chain ID between a Signer and Provider in SignerMiddleware
[#1095](https://gakonst/ethers-rs/pull/1095)
- Add BlockNative gas oracle [#1175](https://github.com/gakonst/ethers-rs/pull/1175)
### 0.6.0
- add the missing constructor for `Timelag` middleware via
[#568](https://github.com/gakonst/ethers-rs/pull/568)
- Removes GasNow as a gas price oracle
[#508](https://github.com/gakonst/ethers-rs/pull/508)
- add initialize_nonce public function to initialize NonceMiddleManager
### 0.5.3
- Added Time Lagged middleware
[#457](https://github.com/gakonst/ethers-rs/pull/457)

File diff suppressed because it is too large Load Diff

View File

@ -134,14 +134,13 @@ include one or more tests to ensure that ethers-rs does not regress in the futur
#### Unit Tests #### Unit Tests
Functions which have very specific tasks should be unit tested. We encourage using Functions which have very specific tasks should be unit tested. We encourage using
table tests to cover a large number of cases in a succinct readable manner. A good example table tests to cover a large number of cases in a succinct readable manner. A good
is the [create2](https://github.com/gakonst/ethers-rs/blob/1d7bdef0bd792867454da28c4e9c193681295fb2/ethers-core/src/utils/mod.rs#L110-L163) unit tests. example is the [utils](./ethers-core/src/utils/mod.rs#L647) unit tests.
#### Integration tests #### Integration tests
Integration tests go in the same crate as the code they are testing. Each sub Integration tests go in the same crate as the code they are testing, in the
crate should have a `dev-dependency` on `ethers` itself. This makes all `tests/it/` directory.
utilities available to use in tests, no matter the crate being tested.
The best strategy for writing a new integration test is to look at existing The best strategy for writing a new integration test is to look at existing
integration tests in the crate and follow the style. integration tests in the crate and follow the style.
@ -155,27 +154,23 @@ that the example is correct and provides additional test coverage.
The trick to documentation tests is striking a balance between being succinct The trick to documentation tests is striking a balance between being succinct
for a reader to understand and actually testing the API. for a reader to understand and actually testing the API.
Same as with integration tests, when writing a documentation test, the full
`ethers` crate is available. This is especially useful for getting access to the
runtime to run the example.
The documentation tests will be visible from both the crate specific The documentation tests will be visible from both the crate specific
documentation **and** the `ethers` facade documentation via the re-export. The documentation **and** the `ethers` facade documentation via the re-export. The
example should be written from the point of view of a user that is using the example should be written from the point of view of a user that is using the
`ethers` crate. As such, the example should use the API via the facade and not by `ethers` crate.
directly referencing the crate.
The type level example for `ethers_providers::Provider` provides a good example of a The type level example for `ethers_providers::Provider` provides a good example of a
documentation test: documentation test:
````rust ````rust
/// ```no_run /// ```no_run
/// use ethers::providers::{JsonRpcClient, Provider, Http}; /// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
/// use std::convert::TryFrom; /// use ethers_providers::{Middleware, Provider, Http};
/// ///
/// let provider = Provider::<Http>::try_from("https://eth.llamarpc.com").expect("could not instantiate HTTP Provider"); /// let provider = Provider::<Http>::try_from(
/// "https://eth.llamarpc.com"
/// ).expect("could not instantiate HTTP Provider");
/// ///
/// # async fn foo<P: JsonRpcClient>(provider: &Provider<P>) -> Result<(), Box<dyn std::error::Error>> {
/// let block = provider.get_block(100u64).await?; /// let block = provider.get_block(100u64).await?;
/// println!("Got block: {}", serde_json::to_string(&block)?); /// println!("Got block: {}", serde_json::to_string(&block)?);
/// # Ok(()) /// # Ok(())
@ -205,52 +200,15 @@ notes about [commit squashing](#commit-squashing)).
#### Commit message guidelines #### Commit message guidelines
A good commit message should describe what changed and why. Commit messages should follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
specification.
1. The first line should: Here's a few examples from the master branch's commit log:
- contain a short description of the change (preferably 50 characters or less, - feat(abigen): support empty events
and no more than 72 characters) - chore: bump crypto deps
- be entirely in lowercase with the exception of proper nouns, acronyms, and - test: simplify test cleanup
the words that refer to code, like function/variable names - fmt: run rustfmt
- be prefixed with the name of the sub crate being changed (without the `ethers-`
prefix) and start with an imperative verb. If modifying `ethers` proper,
omit the crate prefix.
Examples:
- providers: introduce ENS querying for names and addresses
- re-export the abi, types and utils modules from `ethers_core`
2. Keep the second line blank.
3. Wrap all other lines at 72 columns (except for long URLs).
4. If your patch fixes an open issue, you can add a reference to it at the end
of the log. Use the `Fixes: #` prefix and the issue number. For other
references use `Refs: #`. `Refs` may include multiple issues, separated by a
comma.
Examples:
- `Fixes: #1337`
- `Refs: #1234`
Sample complete commit message:
```txt
subcrate: explain the commit in one line
Body of commit message is a few lines of text, explaining things
in more detail, possibly giving some background about the issue
being fixed, etc.
The body of the commit message can be several paragraphs, and
please do proper word-wrap and keep columns shorter than about
72 characters or so. That way, `git log` will show things
nicely even when it is indented.
Fixes: #1337
Refs: #453, #154
```
### Opening the Pull Request ### Opening the Pull Request
@ -380,17 +338,14 @@ When releasing the workspace:
existing APIs. If so, resolve those issues and make a `minor` change existing APIs. If so, resolve those issues and make a `minor` change
release. Otherwise, if it is necessary to make a breaking release, make a release. Otherwise, if it is necessary to make a breaking release, make a
`major` change release. `major` change release.
2. **Dry run the release** by running `cargo release --workspace <release_type>` 2. **Dry run the release.** Running the `cargo release` command without the
3. **Update the changelog for the crate.** Changelog for all crates go in `--execute` flag will perform a dry run.
[`CHANGELOG.md`](./CHANGELOG.md). Any unreleased changes changelogs should 3. **Release the crate.**
be moved to respective crates released changelogs. Change descriptions Run the `bin/release` script with the `--execute` flag.
may be taken from the Git history, but should be edited to ensure a consistent This will update the package versions in the relevant manifests, create
format, based on [Keep A Changelog][keep-a-changelog]. Other entries in that git tags, automatically generate the [`CHANGELOG.md`](./CHANGELOG.md) file
crate's changelog may also be used for reference. with [git-cliff], and finally publish the crates to `crates.io`.
4. **Release the crate.** Run the following command: For more information, see the top comment in the script file.
```bash
cargo release --workspace <release_type> --execute
```
[keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog/blob/master/CHANGELOG.md [keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog/blob/master/CHANGELOG.md
[git-cliff]: https://github.com/orhun/git-cliff

224
Cargo.lock generated
View File

@ -77,9 +77,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.69" version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
@ -98,13 +98,13 @@ dependencies = [
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.66" version = "0.1.68"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc" checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn 2.0.9",
] ]
[[package]] [[package]]
@ -209,9 +209,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.0.1" version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5dd14596c0e5b954530d0e6f1fd99b89c03e313aa2086e8da4303701a09e1cf" checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1"
[[package]] [[package]]
name = "bitvec" name = "bitvec"
@ -841,9 +841,9 @@ dependencies = [
[[package]] [[package]]
name = "cxx" name = "cxx"
version = "1.0.92" version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72" checksum = "a9c00419335c41018365ddf7e4d5f1c12ee3659ddcf3e01974650ba1de73d038"
dependencies = [ dependencies = [
"cc", "cc",
"cxxbridge-flags", "cxxbridge-flags",
@ -853,9 +853,9 @@ dependencies = [
[[package]] [[package]]
name = "cxx-build" name = "cxx-build"
version = "1.0.92" version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613" checksum = "fb8307ad413a98fff033c8545ecf133e3257747b3bae935e7602aab8aa92d4ca"
dependencies = [ dependencies = [
"cc", "cc",
"codespan-reporting", "codespan-reporting",
@ -863,24 +863,24 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"scratch", "scratch",
"syn 1.0.109", "syn 2.0.9",
] ]
[[package]] [[package]]
name = "cxxbridge-flags" name = "cxxbridge-flags"
version = "1.0.92" version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97" checksum = "edc52e2eb08915cb12596d29d55f0b5384f00d697a646dbd269b6ecb0fbd9d31"
[[package]] [[package]]
name = "cxxbridge-macro" name = "cxxbridge-macro"
version = "1.0.92" version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56" checksum = "631569015d0d8d54e6c241733f944042623ab6df7bc3be7466874b05fcdb1c5f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn 2.0.9",
] ]
[[package]] [[package]]
@ -1054,9 +1054,9 @@ dependencies = [
[[package]] [[package]]
name = "ena" name = "ena"
version = "0.14.1" version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2e5d13ca2353ab7d0230988629def93914a8c4015f621f9b13ed2955614731d" checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1"
dependencies = [ dependencies = [
"log", "log",
] ]
@ -1200,7 +1200,7 @@ dependencies = [
[[package]] [[package]]
name = "ethers" name = "ethers"
version = "2.0.0" version = "2.0.1"
dependencies = [ dependencies = [
"ethers-addressbook", "ethers-addressbook",
"ethers-contract", "ethers-contract",
@ -1210,12 +1210,13 @@ dependencies = [
"ethers-providers", "ethers-providers",
"ethers-signers", "ethers-signers",
"ethers-solc", "ethers-solc",
"serde",
"tokio", "tokio",
] ]
[[package]] [[package]]
name = "ethers-addressbook" name = "ethers-addressbook"
version = "2.0.0" version = "2.0.1"
dependencies = [ dependencies = [
"ethers-core", "ethers-core",
"once_cell", "once_cell",
@ -1225,7 +1226,7 @@ dependencies = [
[[package]] [[package]]
name = "ethers-contract" name = "ethers-contract"
version = "2.0.0" version = "2.0.1"
dependencies = [ dependencies = [
"ethers-contract-abigen", "ethers-contract-abigen",
"ethers-contract-derive", "ethers-contract-derive",
@ -1245,7 +1246,7 @@ dependencies = [
[[package]] [[package]]
name = "ethers-contract-abigen" name = "ethers-contract-abigen"
version = "2.0.0" version = "2.0.1"
dependencies = [ dependencies = [
"Inflector", "Inflector",
"dunce", "dunce",
@ -1272,7 +1273,7 @@ dependencies = [
[[package]] [[package]]
name = "ethers-contract-derive" name = "ethers-contract-derive"
version = "2.0.0" version = "2.0.1"
dependencies = [ dependencies = [
"Inflector", "Inflector",
"ethers-contract-abigen", "ethers-contract-abigen",
@ -1286,7 +1287,7 @@ dependencies = [
[[package]] [[package]]
name = "ethers-core" name = "ethers-core"
version = "2.0.0" version = "2.0.1"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"bincode", "bincode",
@ -1317,7 +1318,7 @@ dependencies = [
[[package]] [[package]]
name = "ethers-etherscan" name = "ethers-etherscan"
version = "2.0.0" version = "2.0.1"
dependencies = [ dependencies = [
"ethers-core", "ethers-core",
"ethers-solc", "ethers-solc",
@ -1336,7 +1337,7 @@ dependencies = [
[[package]] [[package]]
name = "ethers-middleware" name = "ethers-middleware"
version = "2.0.0" version = "2.0.1"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"auto_impl", "auto_impl",
@ -1346,6 +1347,7 @@ dependencies = [
"ethers-providers", "ethers-providers",
"ethers-signers", "ethers-signers",
"ethers-solc", "ethers-solc",
"futures-channel",
"futures-locks", "futures-locks",
"futures-util", "futures-util",
"hex", "hex",
@ -1364,7 +1366,7 @@ dependencies = [
[[package]] [[package]]
name = "ethers-providers" name = "ethers-providers"
version = "2.0.0" version = "2.0.1"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"auto_impl", "auto_impl",
@ -1403,7 +1405,7 @@ dependencies = [
[[package]] [[package]]
name = "ethers-signers" name = "ethers-signers"
version = "2.0.0" version = "2.0.1"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"coins-bip32", "coins-bip32",
@ -1435,7 +1437,7 @@ dependencies = [
[[package]] [[package]]
name = "ethers-solc" name = "ethers-solc"
version = "2.0.0" version = "2.0.1"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"criterion", "criterion",
@ -1474,25 +1476,9 @@ dependencies = [
"yansi", "yansi",
] ]
[[package]]
name = "ethers-wasm"
version = "2.0.0"
dependencies = [
"console_error_panic_hook",
"ethers",
"hex",
"serde",
"serde-wasm-bindgen",
"serde_json",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-test",
"web-sys",
]
[[package]] [[package]]
name = "examples-anvil" name = "examples-anvil"
version = "2.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"ethers", "ethers",
"eyre", "eyre",
@ -1501,14 +1487,14 @@ dependencies = [
[[package]] [[package]]
name = "examples-big-numbers" name = "examples-big-numbers"
version = "2.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"ethers", "ethers",
] ]
[[package]] [[package]]
name = "examples-contracts" name = "examples-contracts"
version = "2.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"ethers", "ethers",
"eyre", "eyre",
@ -1519,7 +1505,7 @@ dependencies = [
[[package]] [[package]]
name = "examples-events" name = "examples-events"
version = "2.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"ethers", "ethers",
"eyre", "eyre",
@ -1542,7 +1528,7 @@ dependencies = [
[[package]] [[package]]
name = "examples-middleware" name = "examples-middleware"
version = "2.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"ethers", "ethers",
@ -1555,7 +1541,7 @@ dependencies = [
[[package]] [[package]]
name = "examples-providers" name = "examples-providers"
version = "2.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"ethers", "ethers",
@ -1570,7 +1556,7 @@ dependencies = [
[[package]] [[package]]
name = "examples-queries" name = "examples-queries"
version = "2.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"ethers", "ethers",
"eyre", "eyre",
@ -1581,7 +1567,7 @@ dependencies = [
[[package]] [[package]]
name = "examples-subscriptions" name = "examples-subscriptions"
version = "2.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"ethers", "ethers",
"eyre", "eyre",
@ -1592,7 +1578,7 @@ dependencies = [
[[package]] [[package]]
name = "examples-transactions" name = "examples-transactions"
version = "2.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"ethers", "ethers",
"eyre", "eyre",
@ -1603,7 +1589,7 @@ dependencies = [
[[package]] [[package]]
name = "examples-wallets" name = "examples-wallets"
version = "2.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"ethers", "ethers",
"eyre", "eyre",
@ -1612,6 +1598,22 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "examples-wasm"
version = "0.0.0"
dependencies = [
"console_error_panic_hook",
"ethers",
"hex",
"serde",
"serde-wasm-bindgen",
"serde_json",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-test",
"web-sys",
]
[[package]] [[package]]
name = "eyre" name = "eyre"
version = "0.6.8" version = "0.6.8"
@ -2114,16 +2116,16 @@ dependencies = [
[[package]] [[package]]
name = "iana-time-zone" name = "iana-time-zone"
version = "0.1.53" version = "0.1.54"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d"
dependencies = [ dependencies = [
"android_system_properties", "android_system_properties",
"core-foundation-sys", "core-foundation-sys",
"iana-time-zone-haiku", "iana-time-zone-haiku",
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
"winapi", "windows",
] ]
[[package]] [[package]]
@ -2192,9 +2194,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.9.2" version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"hashbrown", "hashbrown",
@ -2233,10 +2235,11 @@ dependencies = [
[[package]] [[package]]
name = "io-lifetimes" name = "io-lifetimes"
version = "1.0.6" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb"
dependencies = [ dependencies = [
"hermit-abi 0.3.1",
"libc", "libc",
"windows-sys 0.45.0", "windows-sys 0.45.0",
] ]
@ -2249,9 +2252,9 @@ checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146"
[[package]] [[package]]
name = "is-terminal" name = "is-terminal"
version = "0.4.4" version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e"
dependencies = [ dependencies = [
"hermit-abi 0.3.1", "hermit-abi 0.3.1",
"io-lifetimes", "io-lifetimes",
@ -2472,9 +2475,9 @@ dependencies = [
[[package]] [[package]]
name = "mime" name = "mime"
version = "0.3.16" version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
@ -2646,9 +2649,9 @@ dependencies = [
[[package]] [[package]]
name = "openssl" name = "openssl"
version = "0.10.46" version = "0.10.48"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd2523381e46256e40930512c7fd25562b9eae4812cb52078f155e87217c9d1e" checksum = "518915b97df115dd36109bfa429a48b8f737bd05508cf9588977b599648926d2"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"cfg-if", "cfg-if",
@ -2678,9 +2681,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.81" version = "0.9.83"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "176be2629957c157240f68f61f2d0053ad3a4ecfdd9ebf1e6521d18d9635cf67" checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"cc", "cc",
@ -2691,9 +2694,9 @@ dependencies = [
[[package]] [[package]]
name = "os_str_bytes" name = "os_str_bytes"
version = "6.4.1" version = "6.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267"
[[package]] [[package]]
name = "output_vt100" name = "output_vt100"
@ -3090,9 +3093,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.52" version = "1.0.53"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -3198,9 +3201,9 @@ dependencies = [
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.7.1" version = "1.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -3218,15 +3221,15 @@ dependencies = [
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.6.28" version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.11.14" version = "0.11.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" checksum = "0ba30cc2c0cd02af1222ed216ba659cdb2f879dfe3181852fe7c50b1d0005949"
dependencies = [ dependencies = [
"base64 0.21.0", "base64 0.21.0",
"bytes", "bytes",
@ -3430,9 +3433,9 @@ dependencies = [
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.36.9" version = "0.36.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"errno", "errno",
@ -3507,9 +3510,9 @@ dependencies = [
[[package]] [[package]]
name = "scale-info" name = "scale-info"
version = "2.3.1" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "001cf62ece89779fd16105b5f515ad0e5cedcd5440d3dd806bb067978e7c3608" checksum = "61471dff9096de1d8b2319efed7162081e96793f5ebb147e50db10d50d648a4d"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"derive_more", "derive_more",
@ -3519,9 +3522,9 @@ dependencies = [
[[package]] [[package]]
name = "scale-info-derive" name = "scale-info-derive"
version = "2.3.1" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "303959cf613a6f6efd19ed4b4ad5bf79966a13352716299ad532cfb115f4205c" checksum = "219580e803a66b3f05761fd06f1f879a872444e49ce23f73694d26e5a954c7e6"
dependencies = [ dependencies = [
"proc-macro-crate", "proc-macro-crate",
"proc-macro2", "proc-macro2",
@ -3638,9 +3641,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.156" version = "1.0.158"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4" checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
@ -3658,13 +3661,13 @@ dependencies = [
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.156" version = "1.0.158"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d" checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn 2.0.9",
] ]
[[package]] [[package]]
@ -3680,9 +3683,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_path_to_error" name = "serde_path_to_error"
version = "0.1.10" version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db0969fff533976baadd92e08b1d102c5a3d8a8049eadfd69d4d1e3c5b2ed189" checksum = "f7f05c1d5476066defcdfacce1f52fc3cae3af1d3089727100c02ae92e5abbe0"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@ -4072,22 +4075,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.39" version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.39" version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn 2.0.9",
] ]
[[package]] [[package]]
@ -4267,9 +4270,9 @@ dependencies = [
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.19.7" version = "0.19.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc18466501acd8ac6a3f615dd29a3438f8ca6bb3b19537138b3106e575621274" checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde", "serde",
@ -4442,9 +4445,9 @@ dependencies = [
[[package]] [[package]]
name = "unicode-bidi" name = "unicode-bidi"
version = "0.3.11" version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c" checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
@ -4711,6 +4714,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25"
dependencies = [
"windows-targets",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.42.0" version = "0.42.0"
@ -4794,9 +4806,9 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.3.6" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d020b441f92996c80d94ae9166e8501e59c7bb56121189dc9eab3bd8216966" checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
@ -4857,7 +4869,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6a89568917376ff46a3de7bd0abdac47e9cc8ded4e1018e4a36d071d43a54ad" checksum = "f6a89568917376ff46a3de7bd0abdac47e9cc8ded4e1018e4a36d071d43a54ad"
dependencies = [ dependencies = [
"aes 0.8.2", "aes 0.8.2",
"bitflags 2.0.1", "bitflags 2.0.2",
"cbc", "cbc",
"ccm", "ccm",
"cmac", "cmac",

View File

@ -1,5 +1,5 @@
[workspace.package] [workspace.package]
version = "2.0.0" version = "2.0.1"
edition = "2021" edition = "2021"
rust-version = "1.65" rust-version = "1.65"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
@ -64,18 +64,18 @@ resolver = "2"
[workspace.dependencies] [workspace.dependencies]
# workspace crates # workspace crates
ethers = { version = "2.0.0", path = "ethers", default-features = false } ethers = { version = "2.0.1", path = "ethers", default-features = false }
ethers-addressbook = { version = "2.0.0", path = "ethers-addressbook", default-features = false } ethers-addressbook = { version = "2.0.1", path = "ethers-addressbook", default-features = false }
ethers-contract = { version = "2.0.0", path = "ethers-contract", default-features = false } ethers-contract = { version = "2.0.1", path = "ethers-contract", default-features = false }
ethers-core = { version = "2.0.0", path = "ethers-core", default-features = false } ethers-core = { version = "2.0.1", path = "ethers-core", default-features = false }
ethers-etherscan = { version = "2.0.0", path = "ethers-etherscan", default-features = false } ethers-etherscan = { version = "2.0.1", path = "ethers-etherscan", default-features = false }
ethers-middleware = { version = "2.0.0", path = "ethers-middleware", default-features = false } ethers-middleware = { version = "2.0.1", path = "ethers-middleware", default-features = false }
ethers-providers = { version = "2.0.0", path = "ethers-providers", default-features = false } ethers-providers = { version = "2.0.1", path = "ethers-providers", default-features = false }
ethers-signers = { version = "2.0.0", path = "ethers-signers", default-features = false } ethers-signers = { version = "2.0.1", path = "ethers-signers", default-features = false }
ethers-solc = { version = "2.0.0", path = "ethers-solc", default-features = false } ethers-solc = { version = "2.0.1", path = "ethers-solc", default-features = false }
ethers-contract-abigen = { version = "2.0.0", path = "ethers-contract/ethers-contract-abigen", default-features = false } ethers-contract-abigen = { version = "2.0.1", path = "ethers-contract/ethers-contract-abigen", default-features = false }
ethers-contract-derive = { version = "2.0.0", path = "ethers-contract/ethers-contract-derive", default-features = false } ethers-contract-derive = { version = "2.0.1", path = "ethers-contract/ethers-contract-derive", default-features = false }
# async / async utils # async / async utils
tokio = "1.26" tokio = "1.26"

View File

@ -134,7 +134,7 @@ ethers = { version = "1.0.2", features = ["openssl"] }
## Note on WASM and FFI bindings ## Note on WASM and FFI bindings
You should be able to build a wasm app that uses ethers-rs (see the [example](./examples/ethers-wasm) for reference). If ethers fails to You should be able to build a wasm app that uses ethers-rs (see the [example](./examples/wasm) for reference). If ethers fails to
compile in WASM, please compile in WASM, please
[open an issue](https://github.com/gakonst/ethers-rs/issues/new/choose). [open an issue](https://github.com/gakonst/ethers-rs/issues/new/choose).
There is currently no plan to provide an official JS/TS-accessible library There is currently no plan to provide an official JS/TS-accessible library

View File

@ -1,119 +0,0 @@
#!/usr/bin/env bash
set -e
USAGE="Publish a new release of a ethers-rs crate
USAGE:
$(basename "$0") [OPTIONS] [CRATE] [VERSION]
OPTIONS:
-v, --verbose Use verbose Cargo output
-d, --dry-run Perform a dry run (do not publish or tag the release)
-h, --help Show this help text and exit"
DRY_RUN=""
VERBOSE=""
err() {
echo -e "\e[31m\e[1merror:\e[0m $@" 1>&2;
}
status() {
WIDTH=12
printf "\e[32m\e[1m%${WIDTH}s\e[0m %s\n" "$1" "$2"
}
verify() {
status "Verifying" "if $CRATE v$VERSION can be released"
ACTUAL=$(cargo pkgid | sed -n 's/.*#\(.*\)/\1/p')
if [ "$ACTUAL" != "$VERSION" ]; then
err "expected to release version $VERSION, but Cargo.toml contained $ACTUAL"
exit 1
fi
if git tag -l | grep -Fxq "$TAG" ; then
err "git tag \`$TAG\` already exists"
exit 1
fi
PATH_DEPS=$(grep -F "path = \"" Cargo.toml | sed -e 's/^/ /')
if [ -n "$PATH_DEPS" ]; then
err "crate \`$CRATE\` contained path dependencies:\n$PATH_DEPS"
echo "path dependencies must be removed prior to release"
exit 1
fi
}
release() {
status "Releasing" "$CRATE v$VERSION"
cargo package $VERBOSE
cargo publish $VERBOSE $DRY_RUN
status "Tagging" "$TAG"
if [ -n "$DRY_RUN" ]; then
echo "# git tag $TAG && git push --tags"
else
git tag "$TAG" && git push --tags
fi
}
while [[ $# -gt 0 ]]
do
case "$1" in
-h|--help)
echo "$USAGE"
exit 0
;;
-v|--verbose)
VERBOSE="--verbose"
set +x
shift
;;
-d|--dry-run)
DRY_RUN="--dry-run"
shift
;;
-*)
err "unknown flag \"$1\""
echo "$USAGE"
exit 1
;;
*) # crate or version
if [ -z "$CRATE" ]; then
CRATE="$1"
elif [ -z "$VERSION" ]; then
VERSION="$1"
else
err "unknown positional argument \"$1\""
echo "$USAGE"
exit 1
fi
shift
;;
esac
done
# set -- "${POSITIONAL[@]}"
if [ -z "$VERSION" ]; then
err "no version specified!"
HELP=1
fi
if [ -n "$CRATE" ]; then
TAG="$CRATE-$VERSION"
else
err "no crate specified!"
HELP=1
fi
if [ -n "$HELP" ]; then
echo "$USAGE"
exit 1
fi
if [ -d "$CRATE" ]; then
(cd "$CRATE" && verify && release )
else
err "no such crate \"$CRATE\""
exit 1
fi

131
bin/release Executable file
View File

@ -0,0 +1,131 @@
#!/usr/bin/env bash
# Creates a new release of all the ethers crates in the workspace
#
# Note that this intended be run with the --execute flag, as the dry run will
# perform all the checks that a normal `cargo release` command would.
# This is because this script will execute only a few of the steps because it
# has to run `git-cliff` to create the changelog in between steps, and exclude
# all the example crates from the version bump.
set -e
info() {
printf "\e[34;1minfo\e[0m: %s\n" "$1"
}
throw() {
printf "\e[31;1merror\e[0m: %s\n\n%s\n" "$1" "$USAGE" 1>&2
exit 1
}
exec_or_print() {
if [ "$EXECUTE" ]; then
"$@"
else
echo "Skipping due to dry run. Command:" "$@"
fi
}
USAGE="Create a new release of the ethers workspace crates
Usage:
$(basename "$0") [OPTIONS] <VERSION>
Options:
-s, --sign Sign commits and tag
-v, --verbose Use verbose Cargo output
-e, --execute Actually perform a release. Dry-run mode is the default
-h, --help Show this help text and exit
Arguments:
<VERSION> See 'cargo release --help'. Levels are not supported"
SIGN_COMMIT=""
SIGN_TAG=""
VERBOSE=""
EXECUTE=""
VERSION=""
while [[ $# -gt 0 ]]; do
case $1 in
-s|--sign)
SIGN_COMMIT="--sign-commit"
SIGN_TAG="--sign-tag"
shift
;;
-v|--verbose)
VERBOSE="--verbose"
shift
;;
-x|--execute)
EXECUTE="--execute"
shift
;;
-h|--help)
echo "$USAGE"
exit 0
;;
--)
VERSION="$2"
break
;;
-*)
throw "unrecognized option: '$1'"
;;
*)
if [ "$VERSION" ]; then
throw "only one version can be specified"
else
VERSION="$1"
fi
shift
;;
esac
done
if [ ! "$VERSION" ]; then
throw "a version must be set" 1>&2
fi
# check for necessary binaries
NECESSARY=(cargo-release git-cliff)
MISSING=""
for bin in "${NECESSARY[@]}"; do
if ! command -v "$bin" &>/dev/null; then
MISSING=true
echo "'$bin' is required to run this command, but it is not installed" 1>&2
fi
done
[ "$MISSING" ] && throw "missing necessary binaries"
# exclude examples
WORKSPACE_FLAGS=(--workspace)
for ex in examples/*; do
if [ -d "$ex" ]; then
crate=$(echo "$ex" | tr / -)
WORKSPACE_FLAGS+=(--exclude "$crate")
fi
done
# shellcheck disable=SC2206
COMMON_FLAGS=($VERBOSE $EXECUTE)
info "bumping crate versions"
cargo release version "${WORKSPACE_FLAGS[@]}" "${COMMON_FLAGS[@]}" "$VERSION"
info "creating changelog"
exec_or_print git cliff -t "$VERSION" -o CHANGELOG.md
info "creating git commit"
exec_or_print cargo release commit "${COMMON_FLAGS[@]}" $SIGN_COMMIT
info "publishing crates"
exec_or_print cargo release publish "${COMMON_FLAGS[@]}" "${WORKSPACE_FLAGS[@]}"
info "tagging commits"
cargo release tag "${COMMON_FLAGS[@]}" "${WORKSPACE_FLAGS[@]}" $SIGN_TAG
info "pushing commits and tags to remote"
cargo release push "${COMMON_FLAGS[@]}" "${WORKSPACE_FLAGS[@]}"

View File

@ -1,11 +1,13 @@
# Summary # Summary
# Getting started # Getting started
- [Intro](./getting-started/intro.md) - [Intro](./getting-started/intro.md)
- [Start a new project](./getting-started/start_a_new_project.md) - [Start a new project](./getting-started/start_a_new_project.md)
- [Connect to an Ethereum node](./getting-started/connect_to_an_ethereum_node.md) - [Connect to an Ethereum node](./getting-started/connect_to_an_ethereum_node.md)
# Reference guide # Reference guide
- [Providers](./providers/providers.md) - [Providers](./providers/providers.md)
- [Http](./providers/http.md) - [Http](./providers/http.md)
- [WebSocket](./providers/ws.md) - [WebSocket](./providers/ws.md)
@ -26,16 +28,16 @@
- [Signer](./middleware/signer.md) - [Signer](./middleware/signer.md)
- [Time lag]() - [Time lag]()
- [Transformer]() - [Transformer]()
- [Contracts]() - [Contracts](./contracts/contracts.md)
- [Abigen]() - [Abigen](./contracts/abigen.md)
- [Compile]() - [Compile](./contracts/compile.md)
- [Creating Instances]() - [Creating Instances](./contracts/creating-instances.md)
- [Deploy Anvil]() - [Deploy Anvil](./contracts/deploy-anvil.md)
- [Deploy from ABI and bytecode]() - [Deploy from ABI and bytecode](./contracts/deploy-from-abi-and-bytecode.md)
- [Deploy Moonbeam]() - [Deploy Moonbeam](./contracts/doploy-moonbeam.md)
- [Events]() - [Events](./contracts/events.md)
- [Events with meta]() - [Events with meta](./contracts/events-with-meta.md)
- [Methods]() - [Methods](contracts/methods.md)
- [Events]() - [Events]()
- [Logs and filtering]() - [Logs and filtering]()
- [Solidity topics]() - [Solidity topics]()

5
book/contracts/abigen.md Normal file
View File

@ -0,0 +1,5 @@
# Abigen
```rust
{{#include ../../examples/contracts/examples/abigen.rs}}
```

View File

@ -0,0 +1,5 @@
# Compile
```rust
{{#include ../../examples/contracts/examples/compile.rs}}
```

View File

@ -0,0 +1,29 @@
# Contracts
In ethers-rs, contracts are a way to interact with smart contracts on the Ethereum blockchain through rust bindings, which serve as a robust rust API to these objects.
The ethers-contracts module includes the following features:
- [Abigen](): A module for generating Rust code from Solidity contracts.
- [Compile](): A module for compiling Solidity contracts into bytecode and ABI files.
- [Creating Instances](): A module for creating instances of smart contracts.
- [Deploy Anvil](): A module for deploying smart contracts on the Anvil network.
- [Deploy from ABI and bytecode](): A module for deploying smart contracts from their ABI and bytecode files.
- [Deploy Moonbeam](): A module for deploying smart contracts on the Moonbeam network.
- [Events](): A module for listening to smart contract events.
- [Events with Meta](): A module for listening to smart contract events with metadata.
- [Methods](): A module for calling smart contract methods.
The ethers-contracts module provides a convenient way to work with Ethereum smart contracts in Rust. With this module, you can easily create instances of smart contracts, deploy them to the network, and interact with their methods and events.
The Abigen module allows you to generate Rust code from Solidity contracts, which can save you a lot of time and effort when writing Rust code for Ethereum smart contracts.
The Compile module makes it easy to compile Solidity contracts into bytecode and ABI files, which are required for deploying smart contracts.
The Deploy Anvil and Deploy Moonbeam modules allow you to deploy smart contracts to specific networks, making it easy to test and deploy your smart contracts on the desired network.
The Events and Events with Meta modules allow you to listen to smart contract events and retrieve event data, which is essential for building applications that interact with Ethereum smart contracts.
Finally, the Methods module provides a simple way to call smart contract methods from Rust code, allowing you to interact with smart contracts in a programmatic way.
Overall, the ethers-contracts module provides a comprehensive set of tools for working with Ethereum smart contracts in Rust, making it an essential tool for Rust developers building decentralized applications on the Ethereum network.

View File

@ -0,0 +1,5 @@
# Creating Instances
```rust
{{#include ../../examples/contracts/examples/instances.rs}}
```

View File

@ -0,0 +1,5 @@
# Deploy Anvil
```rust
{{#include ../../examples/contracts/examples/deploy_anvil.rs}}
```

View File

@ -0,0 +1,5 @@
# Deploying a Contract from ABI and Bytecode
```rust
{{#include ../../examples/contracts/examples/deploy_from_abi_and_bytecode.rs}}
```

View File

@ -0,0 +1,5 @@
# Deploy Moonbeam
```rust
{{#include ../../examples/contracts/examples/deploy_moonbeam.rs}}
```

View File

@ -0,0 +1,5 @@
# Events with meta
```rust
{{#include ../../examples/contracts/examples/events_with_meta.rs}}
```

5
book/contracts/events.md Normal file
View File

@ -0,0 +1,5 @@
# Events
```rust
{{#include ../../examples/contracts/examples/events.rs}}
```

View File

@ -0,0 +1,5 @@
# Methods
```rust
{{#include ../../examples/contracts/examples/methods.rs}}
```

83
cliff.toml Normal file
View File

@ -0,0 +1,83 @@
# configuration file for git-cliff
# see https://github.com/orhun/git-cliff#configuration-file
[changelog]
# changelog header
header = """
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
This changelog is automatically generated by [git-cliff](https://github.com/orhun/git-cliff),
which is configured [here](./cliff.toml).
Please do not manually edit this file.\n
"""
# template for the changelog body
# https://tera.netlify.app/docs/#introduction
body = """
{% if version %}\
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
## [Unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | title }}
{% for commit in commits %}
- {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first | split(pat="\\n") | first }}\
{% endfor %}
{% endfor %}\n
"""
# remove the leading and trailing whitespace from the template
trim = true
# changelog footer
footer = """
<!-- generated by git-cliff -->
"""
[git]
# parse the commits based on https://www.conventionalcommits.org
conventional_commits = true
# filter out the commits that are not conventional
filter_unconventional = false
# process each line of a commit as an individual commit
split_commits = false
# regex for preprocessing the commit messages
commit_preprocessors = [
# replace issue numbers
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://github.com/gakonst/ethers-rs/issues/${2}))" },
]
# regex for parsing and grouping commits
commit_parsers = [
{ message = "^feat", group = "Features" },
{ message = "^fix", group = "Bug Fixes" },
{ message = "^doc", group = "Documentation" },
{ message = "(.*deps)|(.*dependencies)|(.*[Bb]ump)", group = "Depedencies" },
{ message = "^perf", group = "Performance" },
{ message = "^refactor", group = "Refactor" },
{ message = "(^style)|(.*fmt)", group = "Styling" },
{ message = "^test", group = "Testing" },
{ message = ".*release", skip = true },
{ message = "^revert", skip = true },
{ message = "^chore", group = "Miscellaneous Tasks" },
{ message = ".*", group = "Other" },
]
# protect breaking changes from being skipped due to matching a skipping commit_parser
protect_breaking_commits = true
# filter out the commits that are not matched by commit parsers
filter_commits = false
# glob pattern for matching git tags
tag_pattern = "*"
# regex for skipping tags
# skip_tags = "v0.1.0-beta.1"
# regex for ignoring tags
ignore_tags = "^ethers.*"
# sort the tags topologically
topo_order = false
# sort the commits inside sections by oldest/newest order
sort_commits = "newest"
# limit the number of commits included in the changelog.
# limit_commits = 42

View File

@ -1,5 +1,6 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
#![deny(unsafe_code, rustdoc::broken_intra_doc_links)] #![deny(unsafe_code, rustdoc::broken_intra_doc_links)]
#![cfg_attr(docsrs, feature(doc_cfg))]
pub use ethers_core::types::{Address, Chain}; pub use ethers_core::types::{Address, Chain};

View File

@ -40,9 +40,9 @@ ethers-contract-abigen = { workspace = true, optional = true }
ethers-contract-derive = { workspace = true, optional = true } ethers-contract-derive = { workspace = true, optional = true }
[dev-dependencies] [dev-dependencies]
ethers-providers = { workspace = true, features = ["ws"] }
ethers-signers.workspace = true ethers-signers.workspace = true
ethers-solc.workspace = true ethers-solc.workspace = true
ethers-providers = { workspace = true, features = ["ws"] }
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
tokio = { workspace = true, features = ["macros"] } tokio = { workspace = true, features = ["macros"] }

View File

@ -9,6 +9,7 @@
#![deny(rustdoc::broken_intra_doc_links, missing_docs, unsafe_code)] #![deny(rustdoc::broken_intra_doc_links, missing_docs, unsafe_code)]
#![warn(unreachable_pub)] #![warn(unreachable_pub)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(test)] #[cfg(test)]
#[allow(missing_docs)] #[allow(missing_docs)]

View File

@ -263,14 +263,15 @@ fn _derive_builtin_traits_struct(
} }
} }
/// Recurses on the type until it reaches the struct tuple `ParamType`.
fn get_struct_params<'a>(s_ty: &StructFieldType, ty: &'a ParamType) -> &'a [ParamType] { fn get_struct_params<'a>(s_ty: &StructFieldType, ty: &'a ParamType) -> &'a [ParamType] {
match (s_ty, ty) { match (s_ty, ty) {
(StructFieldType::Type(_), ParamType::Tuple(params)) => params, (_, ParamType::Tuple(params)) => params,
(StructFieldType::Array(s_ty), ParamType::Array(ty)) => get_struct_params(s_ty, ty), (
(StructFieldType::FixedArray(s_ty, _), ParamType::FixedArray(ty, _)) => { StructFieldType::Array(s_ty) | StructFieldType::FixedArray(s_ty, _),
get_struct_params(s_ty, ty) ParamType::Array(param) | ParamType::FixedArray(param, _),
} ) => get_struct_params(s_ty, param),
_ => unreachable!(), _ => unreachable!("Unhandled struct field: {s_ty:?} | {ty:?}"),
} }
} }

View File

@ -2,6 +2,7 @@
#![deny(missing_docs, unsafe_code, unused_crate_dependencies)] #![deny(missing_docs, unsafe_code, unused_crate_dependencies)]
#![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::broken_intra_doc_links)]
#![cfg_attr(docsrs, feature(doc_cfg))]
use abigen::Contracts; use abigen::Contracts;
use proc_macro::TokenStream; use proc_macro::TokenStream;

View File

@ -75,7 +75,6 @@ pub type Contract<M> = ContractInstance<std::sync::Arc<M>, M>;
/// }; /// };
/// use ethers_contract::Contract; /// use ethers_contract::Contract;
/// use ethers_providers::{Provider, Http}; /// use ethers_providers::{Provider, Http};
/// use ethers_signers::Wallet;
/// use std::{convert::TryFrom, sync::Arc}; /// use std::{convert::TryFrom, sync::Arc};
/// ///
/// # async fn foo() -> Result<(), Box<dyn std::error::Error>> { /// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
@ -122,7 +121,6 @@ pub type Contract<M> = ContractInstance<std::sync::Arc<M>, M>;
/// use ethers_core::{abi::Abi, types::Address}; /// use ethers_core::{abi::Abi, types::Address};
/// use ethers_contract::{Contract, EthEvent}; /// use ethers_contract::{Contract, EthEvent};
/// use ethers_providers::{Provider, Http, Middleware}; /// use ethers_providers::{Provider, Http, Middleware};
/// use ethers_signers::Wallet;
/// use std::{convert::TryFrom, sync::Arc}; /// use std::{convert::TryFrom, sync::Arc};
/// use ethers_core::abi::{Detokenize, Token, InvalidOutputType}; /// use ethers_core::abi::{Detokenize, Token, InvalidOutputType};
/// # // this is a fake address used just for this example /// # // this is a fake address used just for this example

View File

@ -323,7 +323,6 @@ where
/// use ethers_solc::Solc; /// use ethers_solc::Solc;
/// use ethers_contract::ContractFactory; /// use ethers_contract::ContractFactory;
/// use ethers_providers::{Provider, Http}; /// use ethers_providers::{Provider, Http};
/// use ethers_signers::Wallet;
/// use std::convert::TryFrom; /// use std::convert::TryFrom;
/// ///
/// # async fn foo() -> Result<(), Box<dyn std::error::Error>> { /// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {

View File

@ -2,6 +2,7 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
#![deny(unsafe_code)] #![deny(unsafe_code)]
#![warn(missing_docs)] #![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[path = "contract.rs"] #[path = "contract.rs"]
mod _contract; mod _contract;

View File

@ -8,7 +8,7 @@ use ethers_core::{
}; };
use ethers_providers::{MockProvider, Provider}; use ethers_providers::{MockProvider, Provider};
use ethers_solc::Solc; use ethers_solc::Solc;
use std::sync::Arc; use std::{fmt::Debug, hash::Hash, sync::Arc};
const fn assert_codec<T: AbiDecode + AbiEncode>() {} const fn assert_codec<T: AbiDecode + AbiEncode>() {}
const fn assert_tokenizeable<T: Tokenizable>() {} const fn assert_tokenizeable<T: Tokenizable>() {}
@ -16,7 +16,12 @@ const fn assert_call<T: AbiEncode + AbiDecode + Default + Tokenizable>() {}
const fn assert_event<T: EthEvent>() {} const fn assert_event<T: EthEvent>() {}
const fn assert_clone<T: Clone>() {} const fn assert_clone<T: Clone>() {}
const fn assert_default<T: Default>() {} const fn assert_default<T: Default>() {}
const fn assert_builtin<T: std::fmt::Debug + PartialEq + Eq + std::hash::Hash>() {} const fn assert_builtin<T: Debug + PartialEq + Eq + Hash>() {}
const fn assert_struct<T>()
where
T: AbiEncode + AbiDecode + Tokenizable + Clone + Default + Debug + PartialEq + Eq + Hash,
{
}
#[test] #[test]
fn can_generate_human_readable() { fn can_generate_human_readable() {
@ -25,7 +30,7 @@ fn can_generate_human_readable() {
r#"[ r#"[
event ValueChanged(address indexed author, string oldValue, string newValue) event ValueChanged(address indexed author, string oldValue, string newValue)
]"#, ]"#,
event_derives(serde::Deserialize, serde::Serialize) derives(serde::Deserialize, serde::Serialize)
); );
assert_eq!("ValueChanged", ValueChangedFilter::name()); assert_eq!("ValueChanged", ValueChangedFilter::name());
assert_eq!("ValueChanged(address,string,string)", ValueChangedFilter::abi_signature()); assert_eq!("ValueChanged(address,string,string)", ValueChangedFilter::abi_signature());
@ -43,13 +48,13 @@ fn can_generate_human_readable_multiple() {
r#"[ r#"[
event ValueChanged1(address indexed author, string oldValue, string newValue) event ValueChanged1(address indexed author, string oldValue, string newValue)
]"#, ]"#,
event_derives(serde::Deserialize, serde::Serialize); derives(serde::Deserialize, serde::Serialize);
SimpleContract2, SimpleContract2,
r#"[ r#"[
event ValueChanged2(address indexed author, string oldValue, string newValue) event ValueChanged2(address indexed author, string oldValue, string newValue)
]"#, ]"#,
event_derives(serde::Deserialize, serde::Serialize) derives(serde::Deserialize, serde::Serialize)
); );
assert_eq!("ValueChanged1", ValueChanged1Filter::name()); assert_eq!("ValueChanged1", ValueChanged1Filter::name());
assert_eq!("ValueChanged1(address,string,string)", ValueChanged1Filter::abi_signature()); assert_eq!("ValueChanged1(address,string,string)", ValueChanged1Filter::abi_signature());
@ -66,7 +71,7 @@ fn can_generate_structs_readable() {
struct Addresses {address[] addr; string s;} struct Addresses {address[] addr; string s;}
event ValueChanged(Value indexed old, Value newValue, Addresses _a) event ValueChanged(Value indexed old, Value newValue, Addresses _a)
]"#, ]"#,
event_derives(serde::Deserialize, serde::Serialize) derives(serde::Deserialize, serde::Serialize)
); );
let addr = Addresses { let addr = Addresses {
addr: vec!["eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee".parse().unwrap()], addr: vec!["eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee".parse().unwrap()],
@ -97,7 +102,7 @@ fn can_generate_structs_with_arrays_readable() {
struct Addresses {address[] addr; string s;} struct Addresses {address[] addr; string s;}
event ValueChanged(Value indexed old, Value newValue, Addresses[] _a) event ValueChanged(Value indexed old, Value newValue, Addresses[] _a)
]"#, ]"#,
event_derives(serde::Deserialize, serde::Serialize) derives(serde::Deserialize, serde::Serialize)
); );
assert_eq!( assert_eq!(
"ValueChanged((address,string),(address,string),(address[],string)[])", "ValueChanged((address,string),(address,string),(address[],string)[])",
@ -113,15 +118,32 @@ fn can_generate_internal_structs() {
abigen!( abigen!(
VerifierContract, VerifierContract,
"ethers-contract/tests/solidity-contracts/verifier_abi.json", "ethers-contract/tests/solidity-contracts/verifier_abi.json",
event_derives(serde::Deserialize, serde::Serialize) derives(serde::Deserialize, serde::Serialize)
); );
assert_tokenizeable::<VerifyingKey>(); assert_struct::<VerifyingKey>();
assert_tokenizeable::<G1Point>(); assert_struct::<G1Point>();
assert_tokenizeable::<G2Point>(); assert_struct::<G2Point>();
}
assert_codec::<VerifyingKey>(); #[test]
assert_codec::<G1Point>(); fn can_generate_internal_structs_2() {
assert_codec::<G2Point>(); abigen!(Beefy, "./tests/solidity-contracts/BeefyV1.json");
assert_struct::<AuthoritySetCommitment>();
assert_struct::<BeefyConsensusState>();
assert_struct::<ProofNode>();
assert_struct::<BeefyConsensusProof>();
let s =
AuthoritySetCommitment { id: U256::from(1), len: U256::from(2), root: Default::default() };
let _encoded = AbiEncode::encode(s.clone());
let s = BeefyConsensusState { current_authority_set: s, ..Default::default() };
let _encoded = AbiEncode::encode(s);
// tuple[][]
let node = ProofNode::default();
let s = BeefyConsensusProof { authorities_proof: vec![vec![node; 2]; 2], ..Default::default() };
let _encoded = AbiEncode::encode(s);
} }
#[test] #[test]
@ -133,11 +155,11 @@ fn can_generate_internal_structs_multiple() {
abigen!( abigen!(
VerifierContract, VerifierContract,
"ethers-contract/tests/solidity-contracts/verifier_abi.json", "ethers-contract/tests/solidity-contracts/verifier_abi.json",
event_derives(serde::Deserialize, serde::Serialize); derives(serde::Deserialize, serde::Serialize);
MyOtherVerifierContract, MyOtherVerifierContract,
"ethers-contract/tests/solidity-contracts/verifier_abi.json", "ethers-contract/tests/solidity-contracts/verifier_abi.json",
event_derives(serde::Deserialize, serde::Serialize); derives(serde::Deserialize, serde::Serialize);
); );
} }
assert_tokenizeable::<VerifyingKey>(); assert_tokenizeable::<VerifyingKey>();
@ -207,7 +229,7 @@ fn can_generate_human_readable_with_structs() {
function bar(uint256 x, uint256 y, address addr) function bar(uint256 x, uint256 y, address addr)
yeet(uint256,uint256,address) yeet(uint256,uint256,address)
]"#, ]"#,
event_derives(serde::Deserialize, serde::Serialize) derives(serde::Deserialize, serde::Serialize)
); );
assert_tokenizeable::<Foo>(); assert_tokenizeable::<Foo>();
assert_codec::<Foo>(); assert_codec::<Foo>();

View File

@ -5,10 +5,7 @@ use ethers_contract::{
}; };
use ethers_core::{ use ethers_core::{
abi::{encode, AbiEncode, Token, Tokenizable}, abi::{encode, AbiEncode, Token, Tokenizable},
types::{ types::{Address, BlockId, Bytes, Filter, ValueOrArray, H160, H256, U256},
transaction::eip712::Eip712, Address, BlockId, Bytes, Filter, ValueOrArray, H160, H256,
I256, U256,
},
utils::{keccak256, Anvil}, utils::{keccak256, Anvil},
}; };
use ethers_providers::{Http, Middleware, MiddlewareError, Provider, StreamExt, Ws}; use ethers_providers::{Http, Middleware, MiddlewareError, Provider, StreamExt, Ws};
@ -330,7 +327,6 @@ async fn call_past_hash_test() {
} }
#[tokio::test] #[tokio::test]
#[cfg(feature = "abigen")]
async fn watch_events() { async fn watch_events() {
let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
let anvil = Anvil::new().spawn(); let anvil = Anvil::new().spawn();

View File

@ -0,0 +1 @@
[{"inputs":[{"internalType":"contract ISMPHost","name":"host","type":"address"},{"components":[{"internalType":"uint256","name":"latestHeight","type":"uint256"},{"internalType":"uint256","name":"latestTimestamp","type":"uint256"},{"internalType":"uint256","name":"frozenHeight","type":"uint256"},{"internalType":"bytes32","name":"latestHeadsRoot","type":"bytes32"},{"internalType":"uint256","name":"beefyActivationBlock","type":"uint256"},{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"len","type":"uint256"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"internalType":"struct AuthoritySetCommitment","name":"currentAuthoritySet","type":"tuple"},{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"len","type":"uint256"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"internalType":"struct AuthoritySetCommitment","name":"nextAuthoritySet","type":"tuple"}],"internalType":"struct BeefyConsensusState","name":"trustedState","type":"tuple"},{"components":[{"components":[{"components":[{"components":[{"internalType":"bytes2","name":"id","type":"bytes2"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Payload[]","name":"payload","type":"tuple[]"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"uint256","name":"validatorSetId","type":"uint256"}],"internalType":"struct Commitment","name":"commitment","type":"tuple"},{"components":[{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint256","name":"authorityIndex","type":"uint256"}],"internalType":"struct Signature[]","name":"signatures","type":"tuple[]"}],"internalType":"struct SignedCommitment","name":"signedCommitment","type":"tuple"},{"components":[{"internalType":"uint256","name":"version","type":"uint256"},{"internalType":"uint256","name":"parentNumber","type":"uint256"},{"internalType":"bytes32","name":"parentHash","type":"bytes32"},{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"len","type":"uint256"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"internalType":"struct AuthoritySetCommitment","name":"nextAuthoritySet","type":"tuple"},{"internalType":"bytes32","name":"extra","type":"bytes32"},{"internalType":"uint256","name":"kIndex","type":"uint256"}],"internalType":"struct BeefyMmrLeaf","name":"latestMmrLeaf","type":"tuple"},{"internalType":"bytes32[]","name":"mmrProof","type":"bytes32[]"},{"components":[{"internalType":"uint256","name":"k_index","type":"uint256"},{"internalType":"bytes32","name":"node","type":"bytes32"}],"internalType":"struct ProofNode[][]","name":"authoritiesProof","type":"tuple[][]"},{"internalType":"bytes","name":"header","type":"bytes"},{"internalType":"uint256","name":"headsIndex","type":"uint256"},{"internalType":"bytes[]","name":"extrinsicProof","type":"bytes[]"},{"internalType":"bytes","name":"timestampExtrinsic","type":"bytes"}],"internalType":"struct BeefyConsensusProof","name":"proof","type":"tuple"}],"name":"VerifyConsensus","outputs":[{"components":[{"internalType":"uint256","name":"latestHeight","type":"uint256"},{"internalType":"uint256","name":"latestTimestamp","type":"uint256"},{"internalType":"uint256","name":"frozenHeight","type":"uint256"},{"internalType":"bytes32","name":"latestHeadsRoot","type":"bytes32"},{"internalType":"uint256","name":"beefyActivationBlock","type":"uint256"},{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"len","type":"uint256"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"internalType":"struct AuthoritySetCommitment","name":"currentAuthoritySet","type":"tuple"},{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"len","type":"uint256"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"internalType":"struct AuthoritySetCommitment","name":"nextAuthoritySet","type":"tuple"}],"internalType":"struct BeefyConsensusState","name":"","type":"tuple"},{"components":[{"components":[{"internalType":"uint256","name":"stateMachineId","type":"uint256"},{"internalType":"uint256","name":"height","type":"uint256"}],"internalType":"struct StateMachineHeight","name":"height","type":"tuple"},{"components":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"internalType":"struct StateCommitment","name":"commitment","type":"tuple"}],"internalType":"struct IntermediateState","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"}]

View File

@ -2,6 +2,7 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
#![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::broken_intra_doc_links)]
#![cfg_attr(not(target_arch = "wasm32"), deny(unused_crate_dependencies))] #![cfg_attr(not(target_arch = "wasm32"), deny(unused_crate_dependencies))]
#![cfg_attr(docsrs, feature(doc_cfg))]
pub mod types; pub mod types;

View File

@ -4,7 +4,7 @@ mod noop;
mod pre_state; mod pre_state;
pub use self::{ pub use self::{
call::{CallConfig, CallFrame}, call::{CallConfig, CallFrame, CallLogFrame},
four_byte::FourByteFrame, four_byte::FourByteFrame,
noop::NoopFrame, noop::NoopFrame,
pre_state::{PreStateConfig, PreStateFrame}, pre_state::{PreStateConfig, PreStateFrame},

View File

@ -32,11 +32,11 @@ pub struct CallFrame {
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct CallLogFrame { pub struct CallLogFrame {
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
address: Option<Address>, pub address: Option<Address>,
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
topics: Option<Vec<H256>>, pub topics: Option<Vec<H256>>,
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
data: Option<Bytes>, pub data: Option<Bytes>,
} }
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,5 +1,6 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
#![deny(unsafe_code, rustdoc::broken_intra_doc_links)] #![deny(unsafe_code, rustdoc::broken_intra_doc_links)]
#![cfg_attr(docsrs, feature(doc_cfg))]
use crate::errors::{is_blocked_by_cloudflare_response, is_cloudflare_security_challenge}; use crate::errors::{is_blocked_by_cloudflare_response, is_cloudflare_security_challenge};
use contract::ContractMetadata; use contract::ContractMetadata;

View File

@ -36,6 +36,7 @@ serde.workspace = true
thiserror.workspace = true thiserror.workspace = true
futures-util.workspace = true futures-util.workspace = true
futures-locks.workspace = true futures-locks.workspace = true
futures-channel.workspace = true
tracing.workspace = true tracing.workspace = true
tracing-futures.workspace = true tracing-futures.workspace = true
instant.workspace = true instant.workspace = true

View File

@ -1,21 +1,28 @@
mod geometric; mod geometric;
use ethers_core::types::transaction::eip2718::TypedTransaction;
pub use geometric::GeometricGasPrice; pub use geometric::GeometricGasPrice;
mod linear; mod linear;
pub use linear::LinearGasPrice; pub use linear::LinearGasPrice;
use async_trait::async_trait; use async_trait::async_trait;
use ethers_core::types::{BlockId, TransactionRequest, TxHash, U256};
use ethers_providers::{interval, Middleware, MiddlewareError, PendingTransaction, StreamExt}; use futures_channel::oneshot;
use futures_util::lock::Mutex; use futures_util::{lock::Mutex, select_biased};
use instant::Instant; use instant::Instant;
use std::{pin::Pin, sync::Arc}; use std::{pin::Pin, sync::Arc};
use thiserror::Error; use thiserror::Error;
use tracing_futures::Instrument;
use ethers_core::types::{
transaction::eip2718::TypedTransaction, BlockId, TransactionRequest, TxHash, U256,
};
use ethers_providers::{interval, Middleware, MiddlewareError, PendingTransaction, StreamExt};
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio::spawn; use tokio::spawn;
type ToEscalate = Arc<Mutex<Vec<(TxHash, TransactionRequest, Instant, Option<BlockId>)>>>;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
type WatcherFuture<'a> = Pin<Box<dyn futures_util::stream::Stream<Item = ()> + 'a>>; type WatcherFuture<'a> = Pin<Box<dyn futures_util::stream::Stream<Item = ()> + 'a>>;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -29,7 +36,34 @@ pub trait GasEscalator: Send + Sync + std::fmt::Debug {
fn get_gas_price(&self, initial_price: U256, time_elapsed: u64) -> U256; fn get_gas_price(&self, initial_price: U256, time_elapsed: u64) -> U256;
} }
#[derive(Debug, Clone)] #[derive(Error, Debug)]
/// Error thrown when the GasEscalator interacts with the blockchain
pub enum GasEscalatorError<M: Middleware> {
#[error("{0}")]
/// Thrown when an internal middleware errors
MiddlewareError(M::Error),
#[error("Gas escalation is only supported for EIP2930 or Legacy transactions")]
UnsupportedTxType,
}
// Boilerplate
impl<M: Middleware> MiddlewareError for GasEscalatorError<M> {
type Inner = M::Error;
fn from_err(src: M::Error) -> GasEscalatorError<M> {
GasEscalatorError::MiddlewareError(src)
}
fn as_inner(&self) -> Option<&Self::Inner> {
match self {
GasEscalatorError::MiddlewareError(e) => Some(e),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy)]
/// The frequency at which transactions will be bumped /// The frequency at which transactions will be bumped
pub enum Frequency { pub enum Frequency {
/// On a per block basis using the eth_newBlock filter /// On a per block basis using the eth_newBlock filter
@ -39,9 +73,49 @@ pub enum Frequency {
} }
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct GasEscalatorMiddlewareInternal<M> {
pub(crate) inner: Arc<M>,
/// The transactions which are currently being monitored for escalation
#[allow(clippy::type_complexity)]
pub txs: ToEscalate,
_background: oneshot::Sender<()>,
}
#[derive(Debug, Clone)]
/// A Gas escalator allows bumping transactions' gas price to avoid getting them /// A Gas escalator allows bumping transactions' gas price to avoid getting them
/// stuck in the memory pool. /// stuck in the memory pool.
/// ///
/// GasEscalator runs a background task which monitors the blockchain for tx
/// confirmation, and bumps fees over time if txns do not occur. This task
/// periodically loops over a stored history of sent transactions, and checks
/// if any require fee bumps. If so, it will resend the same transaction with a
/// higher fee.
///
/// Using [`GasEscalatorMiddleware::new`] will create a new instance of the
/// background task. Using [`GasEscalatorMiddleware::clone`] will crate a new
/// instance of the middleware, but will not create a new background task. The
/// background task is shared among all clones.
///
/// ## Footgun
///
/// If you drop the middleware, the background task will be dropped as well,
/// and any transactions you have sent will stop escalating. We recommend
/// holding an instance of the middleware throughout your application's
/// lifecycle, or leaking an `Arc` of it so that it is never dropped.
///
/// ## Outstanding issue
///
/// This task is fallible, and will stop if the provider's connection is lost.
/// If this happens, the middleware will become unable to properly escalate gas
/// prices. Transactions will still be dispatched, but no fee-bumping will
/// happen. This will also cause a memory leak, as the middleware will keep
/// appending to the list of transactions to escalate (and nothing will ever
/// clear that list).
///
/// We intend to fix this issue in a future release.
///
/// ## Example
///
/// ```no_run /// ```no_run
/// use ethers_providers::{Provider, Http}; /// use ethers_providers::{Provider, Http};
/// use ethers_middleware::{ /// use ethers_middleware::{
@ -63,39 +137,22 @@ pub enum Frequency {
/// let gas_oracle = GasNow::new().category(GasCategory::SafeLow); /// let gas_oracle = GasNow::new().category(GasCategory::SafeLow);
/// let provider = GasOracleMiddleware::new(provider, gas_oracle); /// let provider = GasOracleMiddleware::new(provider, gas_oracle);
/// ``` /// ```
pub struct GasEscalatorMiddleware<M, E> { pub struct GasEscalatorMiddleware<M> {
pub(crate) inner: Arc<M>, pub(crate) inner: Arc<GasEscalatorMiddlewareInternal<M>>,
pub(crate) escalator: E,
/// The transactions which are currently being monitored for escalation
#[allow(clippy::type_complexity)]
pub txs: Arc<Mutex<Vec<(TxHash, TransactionRequest, Instant, Option<BlockId>)>>>,
frequency: Frequency,
}
impl<M, E: Clone> Clone for GasEscalatorMiddleware<M, E> {
fn clone(&self) -> Self {
GasEscalatorMiddleware {
inner: self.inner.clone(),
escalator: self.escalator.clone(),
txs: self.txs.clone(),
frequency: self.frequency.clone(),
}
}
} }
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<M, E> Middleware for GasEscalatorMiddleware<M, E> impl<M> Middleware for GasEscalatorMiddleware<M>
where where
M: Middleware, M: Middleware,
E: GasEscalator,
{ {
type Error = GasEscalatorError<M>; type Error = GasEscalatorError<M>;
type Provider = M::Provider; type Provider = M::Provider;
type Inner = M; type Inner = M;
fn inner(&self) -> &M { fn inner(&self) -> &Self::Inner {
&self.inner &self.inner.inner
} }
async fn send_transaction<T: Into<TypedTransaction> + Send + Sync>( async fn send_transaction<T: Into<TypedTransaction> + Send + Sync>(
@ -103,13 +160,26 @@ where
tx: T, tx: T,
block: Option<BlockId>, block: Option<BlockId>,
) -> Result<PendingTransaction<'_, Self::Provider>, Self::Error> { ) -> Result<PendingTransaction<'_, Self::Provider>, Self::Error> {
self.inner.send_transaction(tx, block).await
}
}
impl<M> GasEscalatorMiddlewareInternal<M>
where
M: Middleware,
{
async fn send_transaction<T: Into<TypedTransaction> + Send + Sync>(
&self,
tx: T,
block: Option<BlockId>,
) -> Result<PendingTransaction<'_, M::Provider>, GasEscalatorError<M>> {
let tx = tx.into(); let tx = tx.into();
let pending_tx = self let pending_tx = self
.inner() .inner
.send_transaction(tx.clone(), block) .send_transaction(tx.clone(), block)
.await .await
.map_err(GasEscalatorError::MiddlewareError)?; .map_err(MiddlewareError::from_err)?;
let tx = match tx { let tx = match tx {
TypedTransaction::Legacy(inner) => inner, TypedTransaction::Legacy(inner) => inner,
@ -125,66 +195,111 @@ where
} }
} }
impl<M, E> GasEscalatorMiddleware<M, E> impl<M> GasEscalatorMiddleware<M>
where where
M: Middleware, M: Middleware,
E: GasEscalator,
{ {
/// Initializes the middleware with the provided gas escalator and the chosen /// Initializes the middleware with the provided gas escalator and the chosen
/// escalation frequency (per block or per second) /// escalation frequency (per block or per second)
#[allow(clippy::let_and_return)] #[allow(clippy::let_and_return)]
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub fn new(inner: M, escalator: E, frequency: Frequency) -> Self pub fn new<E>(inner: M, escalator: E, frequency: Frequency) -> Self
where where
E: Clone + 'static, E: GasEscalator + 'static,
M: Clone + 'static, M: 'static,
{ {
use tracing_futures::Instrument; let (tx, rx) = oneshot::channel();
let inner = Arc::new(inner);
let this = Self { let txs: ToEscalate = Default::default();
inner: Arc::new(inner),
escalator,
frequency,
txs: Arc::new(Mutex::new(Vec::new())),
};
{ let this = Arc::new(GasEscalatorMiddlewareInternal {
let this2 = this.clone(); inner: inner.clone(),
spawn(async move { txs: txs.clone(),
this2.escalate().instrument(tracing::trace_span!("gas-escalation")).await.unwrap(); _background: tx,
}); });
let esc = EscalationTask { inner, escalator, frequency, txs, shutdown: rx };
{
spawn(esc.escalate().instrument(tracing::trace_span!("gas-escalation")));
} }
this Self { inner: this }
}
} }
/// Re-broadcasts pending transactions with a gas price escalator #[derive(Debug)]
pub async fn escalate(&self) -> Result<(), GasEscalatorError<M>> { pub struct EscalationTask<M, E> {
inner: M,
escalator: E,
frequency: Frequency,
txs: ToEscalate,
shutdown: oneshot::Receiver<()>,
}
impl<M, E> EscalationTask<M, E> {
pub fn new(
inner: M,
escalator: E,
frequency: Frequency,
txs: ToEscalate,
shutdown: oneshot::Receiver<()>,
) -> Self {
Self { inner, escalator, frequency, txs, shutdown }
}
async fn escalate(mut self) -> Result<(), GasEscalatorError<M>>
where
M: Middleware,
E: GasEscalator,
{
// the escalation frequency is either on a per-block basis, or on a duration basis // the escalation frequency is either on a per-block basis, or on a duration basis
let mut watcher: WatcherFuture = match self.frequency { let watcher: WatcherFuture = match self.frequency {
Frequency::PerBlock => Box::pin( Frequency::PerBlock => Box::pin(
self.inner self.inner.watch_blocks().await.map_err(MiddlewareError::from_err)?.map(|_| ()),
.watch_blocks()
.await
.map_err(GasEscalatorError::MiddlewareError)?
.map(|_| ()),
), ),
Frequency::Duration(ms) => Box::pin(interval(std::time::Duration::from_millis(ms))), Frequency::Duration(ms) => Box::pin(interval(std::time::Duration::from_millis(ms))),
}; };
while watcher.next().await.is_some() { let mut watcher = watcher.fuse();
let now = Instant::now();
let mut txs = self.txs.lock().await;
let len = txs.len();
loop {
select_biased! {
_ = &mut self.shutdown => {
tracing::debug!("Shutting down escalation task, middleware has gone away");
return Ok(())
}
opt = watcher.next() => {
if opt.is_none() {
tracing::error!("timing future has gone away");
return Ok(());
}
let now = Instant::now();
// We take the contents of the mutex, and then add them back in
// later.
let mut txs: Vec<_> = {
let mut txs = self.txs.lock().await;
std::mem::take(&mut (*txs))
// Lock scope ends
};
let len = txs.len();
// Pop all transactions and re-insert those that have not been included yet // Pop all transactions and re-insert those that have not been included yet
for _ in 0..len { for _ in 0..len {
// this must never panic as we're explicitly within bounds // this must never panic as we're explicitly within bounds
let (tx_hash, mut replacement_tx, time, priority) = let (tx_hash, mut replacement_tx, time, priority) =
txs.pop().expect("should have element in vector"); txs.pop().expect("should have element in vector");
let receipt = self.get_transaction_receipt(tx_hash).await?; let receipt = self
.inner
.get_transaction_receipt(tx_hash)
.await
.map_err(MiddlewareError::from_err)?;
tracing::trace!(tx_hash = ?tx_hash, "checking if exists"); tracing::trace!(tx_hash = ?tx_hash, "checking if exists");
if receipt.is_none() { if receipt.is_none() {
let old_gas_price = replacement_tx.gas_price.expect("gas price must be set"); let old_gas_price = replacement_tx.gas_price.expect("gas price must be set");
// Get the new gas price based on how much time passed since the // Get the new gas price based on how much time passed since the
@ -193,13 +308,14 @@ where
.escalator .escalator
.get_gas_price(old_gas_price, now.duration_since(time).as_secs()); .get_gas_price(old_gas_price, now.duration_since(time).as_secs());
let new_txhash = if new_gas_price != old_gas_price { let new_txhash = if new_gas_price == old_gas_price {
tx_hash
} else {
// bump the gas price // bump the gas price
replacement_tx.gas_price = Some(new_gas_price); replacement_tx.gas_price = Some(new_gas_price);
// the tx hash will be different so we need to update it // the tx hash will be different so we need to update it
match self.inner().send_transaction(replacement_tx.clone(), priority).await match self.inner.send_transaction(replacement_tx.clone(), priority).await {
{
Ok(new_tx_hash) => { Ok(new_tx_hash) => {
let new_tx_hash = *new_tx_hash; let new_tx_hash = *new_tx_hash;
tracing::trace!( tracing::trace!(
@ -220,46 +336,23 @@ where
// push it back to the pending txs vector) // push it back to the pending txs vector)
continue continue
} else { } else {
tracing::error!(
err = %err,
"Killing escalator backend"
);
return Err(GasEscalatorError::MiddlewareError(err)) return Err(GasEscalatorError::MiddlewareError(err))
} }
} }
} }
} else {
tx_hash
}; };
txs.push((new_txhash, replacement_tx, time, priority)); txs.push((new_txhash, replacement_tx, time, priority));
} }
} }
} // after this big ugly loop, we dump everything back in
// we don't replace here, as the vec in the mutex may contain
Ok(()) // items!
} self.txs.lock().await.extend(txs);
} }}
// Boilerplate
impl<M: Middleware> MiddlewareError for GasEscalatorError<M> {
type Inner = M::Error;
fn from_err(src: M::Error) -> GasEscalatorError<M> {
GasEscalatorError::MiddlewareError(src)
}
fn as_inner(&self) -> Option<&Self::Inner> {
match self {
GasEscalatorError::MiddlewareError(e) => Some(e),
_ => None,
} }
} }
} }
#[derive(Error, Debug)]
/// Error thrown when the GasEscalator interacts with the blockchain
pub enum GasEscalatorError<M: Middleware> {
#[error("{0}")]
/// Thrown when an internal middleware errors
MiddlewareError(M::Error),
#[error("Gas escalation is only supported for EIP2930 or Legacy transactions")]
UnsupportedTxType,
}

View File

@ -1,5 +1,6 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
#![deny(unsafe_code, rustdoc::broken_intra_doc_links)] #![deny(unsafe_code, rustdoc::broken_intra_doc_links)]
#![cfg_attr(docsrs, feature(doc_cfg))]
/// The [Gas Escalator middleware](crate::gas_escalator::GasEscalatorMiddleware) /// The [Gas Escalator middleware](crate::gas_escalator::GasEscalatorMiddleware)
/// is used to re-broadcast transactions with an increasing gas price to guarantee /// is used to re-broadcast transactions with an increasing gas price to guarantee

View File

@ -1,8 +1,8 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
#![warn(missing_docs)] #![warn(missing_docs)]
#![deny(unsafe_code, rustdoc::broken_intra_doc_links)] #![deny(unsafe_code, rustdoc::broken_intra_doc_links)]
#![cfg_attr(docsrs, feature(doc_cfg))]
mod ext; mod ext;
pub use ext::*; pub use ext::*;

View File

@ -51,7 +51,7 @@ pub(super) fn sig_from_digest_bytes_trial_recovery(
/// Modify the v value of a signature to conform to eip155 /// Modify the v value of a signature to conform to eip155
pub(super) fn apply_eip155(sig: &mut EthSig, chain_id: u64) { pub(super) fn apply_eip155(sig: &mut EthSig, chain_id: u64) {
let v = (chain_id * 2 + 35) + ((sig.v - 1) % 2); let v = (chain_id * 2 + 35) + sig.v;
sig.v = v; sig.v = v;
} }

View File

@ -1,5 +1,6 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
#![deny(unsafe_code, rustdoc::broken_intra_doc_links)] #![deny(unsafe_code, rustdoc::broken_intra_doc_links)]
#![cfg_attr(docsrs, feature(doc_cfg))]
mod wallet; mod wallet;
pub use wallet::{MnemonicBuilder, Wallet, WalletError}; pub use wallet::{MnemonicBuilder, Wallet, WalletError};

View File

@ -111,14 +111,14 @@ async = [
"futures-util", "futures-util",
"criterion/async_tokio", "criterion/async_tokio",
] ]
svm-solc = ["svm-builds", "sha2"] svm-solc = ["svm", "svm-builds", "sha2"]
# Utilities for creating and testing project workspaces # Utilities for creating and testing project workspaces
project-util = ["tempfile", "fs_extra", "rand"] project-util = ["tempfile", "fs_extra", "rand"]
tests = [] tests = []
openssl = ["svm/openssl"] openssl = ["svm?/openssl"]
rustls = ["svm/rustls"] rustls = ["svm?/rustls"]
# Deprecated # Deprecated
asm = [] asm = []

View File

@ -659,6 +659,7 @@ pub trait ArtifactOutput {
if let Ok(stripped) = rel_candidate.strip_prefix(artifacts_folder) { if let Ok(stripped) = rel_candidate.strip_prefix(artifacts_folder) {
rel_candidate = stripped.to_path_buf(); rel_candidate = stripped.to_path_buf();
} }
#[allow(clippy::redundant_clone)] // false positive
let mut candidate = rel_candidate.clone(); let mut candidate = rel_candidate.clone();
let contract_file = contract_file.as_ref(); let contract_file = contract_file.as_ref();
let mut current_parent = contract_file.parent(); let mut current_parent = contract_file.parent();

View File

@ -1,5 +1,6 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
#![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::broken_intra_doc_links)]
#![cfg_attr(docsrs, feature(doc_cfg))]
pub mod artifacts; pub mod artifacts;
pub mod sourcemap; pub mod sourcemap;

View File

@ -46,7 +46,9 @@
//! [version pragma](https://docs.soliditylang.org/en/develop/layout-of-source-files.html#version-pragma), //! [version pragma](https://docs.soliditylang.org/en/develop/layout-of-source-files.html#version-pragma),
//! which is defined on a per source file basis. //! which is defined on a per source file basis.
use crate::{error::Result, utils, IncludePaths, ProjectPathsConfig, SolcError, Source, Sources}; use crate::{
error::Result, utils, IncludePaths, ProjectPathsConfig, SolcError, SolcVersion, Source, Sources,
};
use parse::{SolData, SolDataUnit, SolImport}; use parse::{SolData, SolDataUnit, SolImport};
use rayon::prelude::*; use rayon::prelude::*;
use semver::VersionReq; use semver::VersionReq;
@ -577,8 +579,7 @@ impl Graph {
// on first error, instead gather all the errors and return a bundled error message instead // on first error, instead gather all the errors and return a bundled error message instead
let mut errors = Vec::new(); let mut errors = Vec::new();
// we also don't want duplicate error diagnostic // we also don't want duplicate error diagnostic
let mut erroneous_nodes = let mut erroneous_nodes = HashSet::with_capacity(self.edges.num_input_files);
std::collections::HashSet::with_capacity(self.edges.num_input_files);
// the sorted list of all versions // the sorted list of all versions
let all_versions = if offline { Solc::installed_versions() } else { Solc::all_versions() }; let all_versions = if offline { Solc::installed_versions() } else { Solc::all_versions() };
@ -596,11 +597,20 @@ impl Graph {
self.retain_compatible_versions(idx, &mut candidates); self.retain_compatible_versions(idx, &mut candidates);
if candidates.is_empty() && !erroneous_nodes.contains(&idx) { if candidates.is_empty() && !erroneous_nodes.contains(&idx) {
// check if the version is even valid
if let Some(Err(version_err)) =
self.node(idx).check_available_version(&all_versions, offline)
{
let f = utils::source_name(&self.node(idx).path, &self.root).display();
errors.push(format!("Encountered invalid solc version in {f}: {version_err}"));
} else {
let mut msg = String::new(); let mut msg = String::new();
self.format_imports_list(idx, &mut msg).unwrap(); self.format_imports_list(idx, &mut msg).unwrap();
errors.push(format!( errors.push(format!(
"Discovered incompatible solidity versions in following\n: {msg}" "Discovered incompatible solidity versions in following\n: {msg}"
)); ));
}
erroneous_nodes.insert(idx); erroneous_nodes.insert(idx);
} else { } else {
// found viable candidates, pick the most recent version that's already installed // found viable candidates, pick the most recent version that's already installed
@ -888,6 +898,38 @@ impl Node {
pub fn unpack(&self) -> (&PathBuf, &Source) { pub fn unpack(&self) -> (&PathBuf, &Source) {
(&self.path, &self.source) (&self.path, &self.source)
} }
/// Checks that the file's version is even available.
///
/// This returns an error if the file's version is invalid semver, or is not available such as
/// 0.8.20, if the highest available version is `0.8.19`
#[allow(dead_code)]
fn check_available_version(
&self,
all_versions: &[SolcVersion],
offline: bool,
) -> Option<std::result::Result<(), SourceVersionError>> {
fn ensure_version(
v: &str,
all_versions: &[SolcVersion],
offline: bool,
) -> std::result::Result<(), SourceVersionError> {
let req: VersionReq =
v.parse().map_err(|err| SourceVersionError::InvalidVersion(v.to_string(), err))?;
if !all_versions.iter().any(|v| req.matches(v.as_ref())) {
return if offline {
Err(SourceVersionError::NoMatchingVersionOffline(req))
} else {
Err(SourceVersionError::NoMatchingVersion(req))
}
}
Ok(())
}
let v = self.data.version.as_ref()?.data();
Some(ensure_version(v, all_versions, offline))
}
} }
/// Helper type for formatting a node /// Helper type for formatting a node
@ -907,6 +949,18 @@ impl<'a> fmt::Display for DisplayNode<'a> {
} }
} }
/// Errors thrown when checking the solc version of a file
#[derive(Debug, thiserror::Error)]
#[allow(unused)]
enum SourceVersionError {
#[error("Failed to parse solidity version {0}: {1}")]
InvalidVersion(String, semver::Error),
#[error("No solc version exists that matches the version requirement: {0}")]
NoMatchingVersion(VersionReq),
#[error("No solc version installed that matches the version requirement: {0}")]
NoMatchingVersionOffline(VersionReq),
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -8,6 +8,7 @@ use ethers_solc::{
}, },
buildinfo::BuildInfo, buildinfo::BuildInfo,
cache::{SolFilesCache, SOLIDITY_FILES_CACHE_FILENAME}, cache::{SolFilesCache, SOLIDITY_FILES_CACHE_FILENAME},
error::SolcError,
info::ContractInfo, info::ContractInfo,
project_util::*, project_util::*,
remappings::Remapping, remappings::Remapping,
@ -1199,6 +1200,27 @@ library MyLib {
let bytecode = &contract.bytecode.as_ref().unwrap().object; let bytecode = &contract.bytecode.as_ref().unwrap().object;
assert!(!bytecode.is_unlinked()); assert!(!bytecode.is_unlinked());
} }
#[test]
fn can_detect_invalid_version() {
let tmp = TempProject::dapptools().unwrap();
let content = r#"
pragma solidity ^0.100.10;
contract A {}
"#;
tmp.add_source("A", content).unwrap();
let out = tmp.compile().unwrap_err();
match out {
SolcError::Message(err) => {
assert_eq!(err, "Encountered invalid solc version in src/A.sol: No solc version exists that matches the version requirement: ^0.100.10");
}
_ => {
unreachable!()
}
}
}
#[test] #[test]
fn can_recompile_with_changes() { fn can_recompile_with_changes() {
let mut tmp = TempProject::dapptools().unwrap(); let mut tmp = TempProject::dapptools().unwrap();

View File

@ -65,9 +65,8 @@ abigen = ["ethers-contract/abigen"]
### abigen without reqwest ### abigen without reqwest
abigen-offline = ["ethers-contract/abigen-offline"] abigen-offline = ["ethers-contract/abigen-offline"]
## solc ## solc
ethers-solc = ["dep:ethers-solc", "ethers-etherscan/ethers-solc"] solc-full = ["ethers-solc/full"]
solc-full = ["ethers-solc?/full"] solc-tests = ["ethers-solc/tests"]
solc-tests = ["ethers-solc?/tests"]
# Deprecated # Deprecated
solc-sha2-asm = [] solc-sha2-asm = []
@ -76,12 +75,15 @@ solc-sha2-asm = []
ethers-addressbook.workspace = true ethers-addressbook.workspace = true
ethers-contract.workspace = true ethers-contract.workspace = true
ethers-core.workspace = true ethers-core.workspace = true
ethers-etherscan.workspace = true ethers-etherscan = { workspace = true, features = ["ethers-solc"] }
ethers-middleware.workspace = true ethers-middleware.workspace = true
ethers-providers.workspace = true ethers-providers.workspace = true
ethers-signers.workspace = true ethers-signers.workspace = true
ethers-solc.workspace = true
ethers-solc = { workspace = true, optional = true }
[dev-dependencies] [dev-dependencies]
tokio = { workspace = true, features = ["macros", "rt"] } serde.workspace = true
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
ethers-contract.workspace = true
ethers-providers = { workspace = true, features = ["rustls"] } # allow https connections
ethers-solc = { workspace = true, features = ["svm-solc"] }

View File

@ -81,6 +81,7 @@
#![warn(missing_debug_implementations, missing_docs, rust_2018_idioms, unreachable_pub)] #![warn(missing_debug_implementations, missing_docs, rust_2018_idioms, unreachable_pub)]
#![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::broken_intra_doc_links)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc(test(no_crate_inject, attr(deny(rust_2018_idioms), allow(dead_code, unused_variables))))] #![doc(test(no_crate_inject, attr(deny(rust_2018_idioms), allow(dead_code, unused_variables))))]
#[doc(inline)] #[doc(inline)]
@ -98,7 +99,6 @@ pub use ethers_providers as providers;
#[doc(inline)] #[doc(inline)]
pub use ethers_signers as signers; pub use ethers_signers as signers;
#[doc(inline)] #[doc(inline)]
#[cfg(feature = "ethers-solc")]
pub use ethers_solc as solc; pub use ethers_solc as solc;
#[doc(inline)] #[doc(inline)]
@ -121,7 +121,6 @@ pub mod prelude {
pub use super::signers::*; pub use super::signers::*;
#[cfg(feature = "ethers-solc")]
pub use super::solc::*; pub use super::solc::*;
} }

158
ethers/tests/eip712.rs Normal file
View File

@ -0,0 +1,158 @@
use ethers::{
contract::{abigen, ContractFactory, Eip712, EthAbiType},
core::{
types::{transaction::eip712::Eip712, Address, Bytes, I256, U256},
utils::{keccak256, Anvil},
},
providers::Provider,
signers::LocalWallet,
solc::Solc,
};
use std::{path::PathBuf, sync::Arc};
#[tokio::test(flavor = "multi_thread")]
async fn test_derive_eip712() {
// Generate Contract ABI Bindings
abigen!(
DeriveEip712Test,
"./ethers-contract/tests/solidity-contracts/derive_eip712_abi.json",
event_derives(serde::Deserialize, serde::Serialize)
);
// Create derived structs
#[derive(Debug, Clone, Eip712, EthAbiType)]
#[eip712(
name = "Eip712Test",
version = "1",
chain_id = 1,
verifying_contract = "0x0000000000000000000000000000000000000001",
salt = "eip712-test-75F0CCte"
)]
struct FooBar {
foo: I256,
bar: U256,
fizz: Bytes,
buzz: [u8; 32],
far: String,
out: Address,
}
// get ABI and bytecode for the DeriveEip712Test contract
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests");
Solc::find_or_install_svm_version("0.6.0").unwrap(); // install solc
let result = Solc::default().compile_source(path).unwrap();
let (abi, bytecode, _) = result
.find("DeriveEip712Test")
.expect("failed to get DeriveEip712Test contract")
.into_parts_or_default();
// launch the network & connect to it
let anvil = Anvil::new().spawn();
let from = anvil.addresses()[0];
let provider = Provider::try_from(anvil.endpoint())
.unwrap()
.with_sender(from)
.interval(std::time::Duration::from_millis(10));
let client = Arc::new(provider);
let factory = ContractFactory::new(abi.clone(), bytecode.clone(), client.clone());
let contract = factory
.deploy(())
.expect("failed to deploy DeriveEip712Test contract")
.legacy()
.send()
.await
.expect("failed to instantiate factory for DeriveEip712 contract");
let addr = contract.address();
let contract = DeriveEip712Test::new(addr, client.clone());
let foo_bar = FooBar {
foo: I256::from(10u64),
bar: U256::from(20u64),
fizz: b"fizz".into(),
buzz: keccak256("buzz"),
far: String::from("space"),
out: Address::from([0; 20]),
};
let derived_foo_bar = derive_eip_712_test::FooBar {
foo: foo_bar.foo,
bar: foo_bar.bar,
fizz: foo_bar.fizz.clone(),
buzz: foo_bar.buzz,
far: foo_bar.far.clone(),
out: foo_bar.out,
};
use ethers::signers::Signer;
let wallet: LocalWallet = anvil.keys()[0].clone().into();
let sig = wallet.sign_typed_data(&foo_bar).await.expect("failed to sign typed data");
let r = <[u8; 32]>::try_from(sig.r)
.expect("failed to parse 'r' value from signature into [u8; 32]");
let s = <[u8; 32]>::try_from(sig.s)
.expect("failed to parse 's' value from signature into [u8; 32]");
let v = u8::try_from(sig.v).expect("failed to parse 'v' value from signature into u8");
let domain_separator = contract
.domain_separator()
.call()
.await
.expect("failed to retrieve domain_separator from contract");
let type_hash =
contract.type_hash().call().await.expect("failed to retrieve type_hash from contract");
let struct_hash = contract
.struct_hash(derived_foo_bar.clone())
.call()
.await
.expect("failed to retrieve struct_hash from contract");
let encoded = contract
.encode_eip_712(derived_foo_bar.clone())
.call()
.await
.expect("failed to retrieve eip712 encoded hash from contract");
let verify = contract
.verify_foo_bar(wallet.address(), derived_foo_bar, r, s, v)
.call()
.await
.expect("failed to verify signed typed data eip712 payload");
assert_eq!(
domain_separator,
foo_bar
.domain()
.expect("failed to return domain_separator from Eip712 implemented struct")
.separator(),
"domain separator does not match contract domain separator!"
);
assert_eq!(
type_hash,
FooBar::type_hash().expect("failed to return type_hash from Eip712 implemented struct"),
"type hash does not match contract struct type hash!"
);
assert_eq!(
struct_hash,
foo_bar
.clone()
.struct_hash()
.expect("failed to return struct_hash from Eip712 implemented struct"),
"struct hash does not match contract struct hash!"
);
assert_eq!(
encoded,
foo_bar
.encode_eip712()
.expect("failed to return domain_separator from Eip712 implemented struct"),
"Encoded value does not match!"
);
assert!(verify, "typed data signature failed!");
}

View File

@ -1,59 +0,0 @@
use crate::simple_storage::SimpleStorage;
use ethers::prelude::*;
use std::{sync::Arc, time::Duration};
static CELO_TESTNET_URL: &str = "https://alfajores-forno.celo-testnet.org";
#[tokio::test]
async fn test_send_transaction() {
// Celo testnet
let provider =
Provider::<Http>::try_from(CELO_TESTNET_URL).unwrap().interval(Duration::from_secs(3));
let chain_id = provider.get_chainid().await.unwrap().as_u64();
// Funded with https://celo.org/developers/faucet
// Please do not drain this account :)
let wallet = "d652abb81e8c686edba621a895531b1f291289b63b5ef09a94f686a5ecdd5db1"
.parse::<LocalWallet>()
.unwrap()
.with_chain_id(chain_id);
let client = SignerMiddleware::new(provider, wallet);
let balance_before = client.get_balance(client.address(), None).await.unwrap();
let tx = TransactionRequest::pay(client.address(), 100);
let _receipt = client.send_transaction(tx, None).await.unwrap().confirmations(3).await.unwrap();
let balance_after = client.get_balance(client.address(), None).await.unwrap();
assert!(balance_before > balance_after);
}
#[tokio::test]
async fn deploy_and_call_contract() {
// Celo testnet
let provider =
Provider::<Http>::try_from(CELO_TESTNET_URL).unwrap().interval(Duration::from_secs(3));
let chain_id = provider.get_chainid().await.unwrap().as_u64();
// Funded with https://celo.org/developers/faucet
let wallet = "58ea5643a78c36926ad5128a6b0d8dfcc7fc705788a993b1c724be3469bc9697"
.parse::<LocalWallet>()
.unwrap()
.with_chain_id(chain_id);
let client = provider.with_signer(wallet);
let client = Arc::new(client);
let deploy_tx = SimpleStorage::deploy(client, ()).unwrap();
let contract = deploy_tx.send().await.unwrap();
let value: U256 = contract.value().call().await.unwrap();
assert_eq!(value, 0.into());
// make a state mutating transaction
// gas estimation costs are sometimes under-reported on celo,
// so we manually set it to avoid failures
let call = contract.set_value(1.into()).gas(100000);
let pending_tx = call.send().await.unwrap();
let _receipt = pending_tx.await.unwrap();
let value: U256 = contract.method("value", ()).unwrap().call().await.unwrap();
assert_eq!(value, 1.into());
}

View File

@ -1,13 +0,0 @@
//! Ethers live tests.
//!
//! If a feature or external binary is added, like Solc, please also update
//! `.github/workflows/ci.yml` at `job.live-test`.
#![cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "celo")]
mod celo;
pub(crate) mod simple_storage {
ethers::contract::abigen!(SimpleStorage, "../testdata/SimpleStorage.json");
}

4
ethers/tests/main.rs Normal file
View File

@ -0,0 +1,4 @@
//! Ethers integration tests.
#![cfg(not(target_arch = "wasm32"))]
mod eip712;

View File

@ -1,6 +1,6 @@
[package] [package]
name = "examples-anvil" name = "examples-anvil"
version = "2.0.0" version = "0.0.0"
publish = false publish = false
authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"] authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "examples-big-numbers" name = "examples-big-numbers"
version = "2.0.0" version = "0.0.0"
publish = false publish = false
authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"] authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "examples-contracts" name = "examples-contracts"
version = "2.0.0" version = "0.0.0"
publish = false publish = false
authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"] authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]
@ -13,7 +13,7 @@ default = ["legacy"]
legacy = ["ethers/legacy"] legacy = ["ethers/legacy"]
[dev-dependencies] [dev-dependencies]
ethers = { workspace = true, features = ["abigen", "ethers-solc", "rustls", "ws"] } ethers = { workspace = true, features = ["abigen", "rustls", "ws"] }
tokio = { workspace = true, features = ["macros"] } tokio = { workspace = true, features = ["macros"] }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "examples-events" name = "examples-events"
version = "2.0.0" version = "0.0.0"
publish = false publish = false
authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"] authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "examples-middleware" name = "examples-middleware"
version = "2.0.0" version = "0.0.0"
publish = false publish = false
authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"] authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "examples-providers" name = "examples-providers"
version = "2.0.0" version = "0.0.0"
publish = false publish = false
authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"] authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "examples-queries" name = "examples-queries"
version = "2.0.0" version = "0.0.0"
publish = false publish = false
authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"] authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "examples-subscriptions" name = "examples-subscriptions"
version = "2.0.0" version = "0.0.0"
publish = false publish = false
authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"] authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "examples-transactions" name = "examples-transactions"
version = "2.0.0" version = "0.0.0"
publish = false publish = false
authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"] authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "examples-wallets" name = "examples-wallets"
version = "2.0.0" version = "0.0.0"
publish = false publish = false
authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"] authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]

View File

@ -1,8 +1,9 @@
[package] [package]
name = "ethers-wasm" name = "examples-wasm"
version = "2.0.0" version = "0.0.0"
authors = ["Matthias Seitz <matthias.seitz@outlook.de>"] authors = ["Matthias Seitz <matthias.seitz@outlook.de>"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
publish = false
rust-version.workspace = true rust-version.workspace = true
edition.workspace = true edition.workspace = true

View File

@ -1,5 +1,5 @@
{ {
"name": "ethers-wasm", "name": "examples-wasm",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"private": "true", "private": "true",
"scripts": { "scripts": {

View File

@ -5,7 +5,7 @@ use ethers::{
signers::Signer, signers::Signer,
types::Chain, types::Chain,
}; };
use ethers_wasm::{utils, SimpleContract}; use examples_wasm::{utils, SimpleContract};
use std::sync::Arc; use std::sync::Arc;
use wasm_bindgen_test::*; use wasm_bindgen_test::*;

3
release.toml Normal file
View File

@ -0,0 +1,3 @@
allow-branch = ["master"]
pre-release-commit-message = "chore: release"
tag-message = "chore: release {{crate_name}} v{{version}}"