diff --git a/.clippy.toml b/.clippy.toml index 6bfeada7..ad0ea4e7 100644 --- a/.clippy.toml +++ b/.clippy.toml @@ -1 +1 @@ -msrv = "1.62" +msrv = "1.64" diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..ef6885e0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +Cargo.lock linguist-generated=true +flake.lock linguist-generated=true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bcfaa4ba..16d62793 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -232,32 +232,7 @@ jobs: - uses: Swatinem/rust-cache@v1 with: cache-on-failure: true - - name: Build all examples - run: | - export PATH=$HOME/bin:$PATH - examples=$(cargo metadata --format-version 1 | \ - jq -c '.packages[] - | select(.name == "ethers") - | .targets[] - | select(.kind[] | contains("example")) - | with_entries(select([.key] - | inside(["name", "required-features"])))' - ) - for example in $examples; do - name="$(echo "$example" | jq -r '.name')" - args=( - -p ethers - --example "$name" - ) - features="$(echo "$example" | jq -r 'try(."required-features" | join(","))')" - if [[ ! -z "$features" ]]; then - args+=(--features "$features") - fi - - echo "building $name" - cargo build "${args[@]}" - done - - name: Run all examples + - name: Build and run all examples run: | export PATH=$HOME/bin:$PATH chmod +x ./scripts/examples.sh diff --git a/.gitignore b/.gitignore index 026535e5..30620b95 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,8 @@ .vscode /.envrc .idea +.nlsp-settings +.devenv* +devenv.local.nix +.direnv +.pre-commit-config.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index f1b26a77..c5a0d996 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Unreleased +- Fix typo in `RwClient` docs for `write_client` method. +- Add support for Geth `debug_traceCall` [#1949](https://github.com/gakonst/ethers-rs/pull/1949) - 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. @@ -103,6 +105,7 @@ - [#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`. ## ethers-contract-abigen @@ -272,6 +275,7 @@ - `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. ### 0.6.0 @@ -340,6 +344,8 @@ ### Unreleased +- Added `openssl` and `rustls` feature flags + [#1961](https://github.com/gakonst/ethers-rs/pull/1961) - Relax Clone requirements when Arc is used [#1183](https://github.com/gakonst/ethers-rs/pull/1183) - Ensure a consistent chain ID between a Signer and Provider in SignerMiddleware diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2d925088..7c0c387f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -121,6 +121,7 @@ cargo check --all-features cargo +nightly fmt --all cargo build --all-features cargo test --all-features +cargo +nightly clippy --all-features ``` ### Tests diff --git a/Cargo.lock b/Cargo.lock index 2b70c1e7..f6f73319 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1420,6 +1420,7 @@ dependencies = [ "hex", "hex-literal", "k256", + "num_enum", "once_cell", "open-fastrlp", "proc-macro2", @@ -1626,6 +1627,111 @@ dependencies = [ "wee_alloc", ] +[[package]] +name = "examples-anvil" +version = "1.0.2" +dependencies = [ + "ethers", + "eyre", + "tokio", +] + +[[package]] +name = "examples-big-numbers" +version = "1.0.2" +dependencies = [ + "ethers", +] + +[[package]] +name = "examples-contracts" +version = "1.0.2" +dependencies = [ + "ethers", + "eyre", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "examples-events" +version = "1.0.2" +dependencies = [ + "ethers", + "eyre", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "examples-middleware" +version = "1.0.2" +dependencies = [ + "async-trait", + "ethers", + "eyre", + "serde_json", + "thiserror", + "tokio", +] + +[[package]] +name = "examples-providers" +version = "1.0.2" +dependencies = [ + "ethers", + "eyre", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "examples-queries" +version = "1.0.2" +dependencies = [ + "ethers", + "eyre", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "examples-subscriptions" +version = "1.0.2" +dependencies = [ + "ethers", + "eyre", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "examples-transactions" +version = "1.0.2" +dependencies = [ + "ethers", + "eyre", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "examples-wallets" +version = "1.0.2" +dependencies = [ + "ethers", + "eyre", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "eyre" version = "0.6.8" @@ -2577,6 +2683,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "num_threads" version = "0.1.3" @@ -3750,9 +3877,9 @@ dependencies = [ [[package]] name = "serial_test" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92761393ee4dc3ff8f4af487bd58f4307c9329bbedea02cac0089ad9c411e153" +checksum = "1c789ec87f4687d022a2405cf46e0cd6284889f1839de292cadeb6c6019506f2" dependencies = [ "dashmap", "futures", @@ -3764,11 +3891,10 @@ dependencies = [ [[package]] name = "serial_test_derive" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b6f5d1c3087fb119617cff2966fe3808a80e5eb59a8c1601d5994d66f4346a5" +checksum = "b64f9e531ce97c88b4778aad0ceee079216071cffec6ac9b904277f8f92e7fe3" dependencies = [ - "proc-macro-error", "proc-macro2", "quote", "syn", diff --git a/Cargo.toml b/Cargo.toml index eff02c36..45c2d1d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "ethers" version = "1.0.2" edition = "2021" -rust-version = "1.64" +rust-version = "1.64" # must also be changed in **/Cargo.toml and .clippy.toml authors = ["Georgios Konstantopoulos "] license = "MIT OR Apache-2.0" readme = "README.md" @@ -21,7 +21,8 @@ members = [ "ethers-middleware", "ethers-etherscan", "ethers-solc", - "examples/ethers-wasm", + # Example crates + "examples/*", ] default-members = [ @@ -63,12 +64,14 @@ eip712 = ["ethers-contract/eip712", "ethers-core/eip712"] ws = ["ethers-providers/ws"] ipc = ["ethers-providers/ipc"] rustls = [ + "ethers-middleware/rustls", "ethers-providers/rustls", "ethers-etherscan/rustls", "ethers-contract/rustls", "ethers-solc/rustls", ] openssl = [ + "ethers-middleware/openssl", "ethers-providers/openssl", "ethers-etherscan/openssl", "ethers-contract/openssl", @@ -84,6 +87,7 @@ abigen = ["ethers-contract/abigen"] ### abigen without reqwest abigen-offline = ["ethers-contract/abigen-offline"] ## solc +ethers-solc = ["dep:ethers-solc", "ethers-etherscan/ethers-solc"] solc-full = ["ethers-solc", "ethers-solc/full"] solc-tests = ["ethers-solc", "ethers-solc/tests"] solc-sha2-asm = ["ethers-solc", "ethers-solc/asm"] @@ -125,62 +129,3 @@ bytes = "1.3.0" # Tell `rustc` to optimize for small code size. opt-level = "s" -[[example]] -name = "abigen" -path = "examples/abigen.rs" -required-features = ["ethers-solc"] - -[[example]] -name = "contract_human_readable" -path = "examples/contract_human_readable.rs" -required-features = ["ethers-solc"] - -[[example]] -name = "contract_with_abi" -path = "examples/contract_with_abi.rs" -required-features = ["ethers-solc"] - -[[example]] -name = "contract_with_abi_and_bytecode" -path = "examples/contract_with_abi_and_bytecode.rs" -required-features = ["ethers-solc"] - -[[example]] -name = "ipc" -path = "examples/ipc.rs" -required-features = ["ipc"] - -[[example]] -name = "ledger" -path = "examples/ledger.rs" -required-features = ["ledger"] - -[[example]] -name = "moonbeam_with_abi" -path = "examples/moonbeam_with_abi.rs" -required-features = ["legacy", "ethers-solc"] - -[[example]] -name = "trezor" -path = "examples/trezor.rs" -required-features = ["trezor"] - -[[example]] -name = "yubi" -path = "examples/yubi.rs" -required-features = ["yubi"] - -[[example]] -name = "paginated_logs" -path = "examples/paginated_logs.rs" -required-features = ["rustls"] - -[[example]] -name = "subscribe_contract_events" -path = "examples/subscribe_contract_events.rs" -required-features = ["rustls", "ws"] - -[[example]] -name = "subscribe_contract_events_with_meta" -path = "examples/subscribe_contract_events_with_meta.rs" -required-features = ["rustls", "ws"] diff --git a/README.md b/README.md index 9d98bb16..c85e9f9e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,12 @@ Extensive documentation and examples are available [here](https://docs.rs/ethers Alternatively, you may clone the repository and run `cd ethers/ && cargo doc --open` -You can also run any of the examples by executing: `cargo run -p ethers --example ` +Examples are organized into individual crates under the `/examples` folder. +You can run any of the examples by executing: +```bash +# cargo run -p --example +cargo run -p examples-big-numbers --example math_operations +``` ## Add ethers-rs to your repository diff --git a/ethers-addressbook/Cargo.toml b/ethers-addressbook/Cargo.toml index 17907527..3e491ffc 100644 --- a/ethers-addressbook/Cargo.toml +++ b/ethers-addressbook/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/gakonst/ethers-rs" keywords = ["ethereum", "web3", "celo", "ethers"] [dependencies] -once_cell = "1.16.0" +once_cell = "1.17.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/ethers-contract/Cargo.toml b/ethers-contract/Cargo.toml index 821fe3bf..aa9cb7ca 100644 --- a/ethers-contract/Cargo.toml +++ b/ethers-contract/Cargo.toml @@ -20,7 +20,7 @@ ethers-derive-eip712 = { version = "^1.0.0", path = "../ethers-core/ethers-deriv serde = { version = "1.0.124", default-features = false } serde_json = { version = "1.0.64", default-features = false } thiserror = { version = "1.0", default-features = false } -once_cell = { version = "1.16.0" } +once_cell = { version = "1.17.0" } pin-project = { version = "1.0.11", default-features = false } futures-util = { version = "^0.3" } hex = { version = "0.4.3", default-features = false, features = ["std"] } diff --git a/ethers-contract/ethers-contract-abigen/Cargo.toml b/ethers-contract/ethers-contract-abigen/Cargo.toml index f69e2f69..164d8b9e 100644 --- a/ethers-contract/ethers-contract-abigen/Cargo.toml +++ b/ethers-contract/ethers-contract-abigen/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "ethers-contract-abigen" version = "1.0.2" +edition = "2021" +rust-version = "1.64" authors = [ "Nicholas Rodrigues Lordello ", "Georgios Konstantopoulos ", ] -edition = "2018" license = "MIT OR Apache-2.0" description = "Code generation for type-safe bindings to Ethereum smart contracts" homepage = "https://docs.rs/ethers" diff --git a/ethers-contract/ethers-contract-abigen/src/multi.rs b/ethers-contract/ethers-contract-abigen/src/multi.rs index f4bd7837..a0c4a21d 100644 --- a/ethers-contract/ethers-contract-abigen/src/multi.rs +++ b/ethers-contract/ethers-contract-abigen/src/multi.rs @@ -31,6 +31,12 @@ impl std::ops::Deref for MultiAbigen { } } +impl std::ops::DerefMut for MultiAbigen { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.abigens + } +} + impl From> for MultiAbigen { fn from(abigens: Vec) -> Self { Self { abigens } @@ -372,6 +378,16 @@ impl MultiBindings { self.expansion.contracts.is_empty() } + /// Specify whether or not to format the code using a locally installed copy + /// of `rustfmt`. + /// + /// Note that in case `rustfmt` does not exist or produces an error, the + /// unformatted code will be used. + pub fn rustfmt(mut self, rustfmt: bool) -> Self { + self.rustfmt = rustfmt; + self + } + fn into_inner(self, single_file: bool) -> MultiBindingsInner { self.expansion.into_bindings(single_file, self.rustfmt) } diff --git a/ethers-contract/ethers-contract-abigen/src/source.rs b/ethers-contract/ethers-contract-abigen/src/source.rs index a1189cf3..2cedae73 100644 --- a/ethers-contract/ethers-contract-abigen/src/source.rs +++ b/ethers-contract/ethers-contract-abigen/src/source.rs @@ -20,6 +20,9 @@ pub enum Source { /// An ABI to be retrieved over HTTP(S). Http(Url), + /// An address of a mainnet contract that has been verified on Bscscan.com. + Bscscan(Address), + /// An address of a mainnet contract that has been verified on Etherscan.io. Etherscan(Address), @@ -54,7 +57,12 @@ impl Source { /// /// - `etherscan:0xXX..XX` or `https://etherscan.io/address/0xXX..XX`: a address or URL of a /// verified contract on Etherscan. - /// + /// - `bscscan:0xXX..XX` or `https://bscscan.io/address/0xXX..XX`: a address or URL of a + /// verified contract on Bscscan. + /// - `polygonscan:0xXX..XX` or `https://polygonscan.io/address/0xXX..XX`: a address or URL of a + /// verified contract on Polygonscan. + /// - `snowtrace:0xXX..XX` or `https://snowtrace.io/address/0xXX..XX`: a address or URL of a + /// verified contract on Snowtrace. /// - `npm:@org/package@1.0.0/path/to/contract.json` an npmjs package with an optional version /// and path (defaulting to the latest version and `index.js`). The contract ABI will be /// retrieved through `unpkg.io`. @@ -99,6 +107,12 @@ impl Source { match url.scheme() { "file" => Ok(Source::local(source)), "http" | "https" => match url.host_str() { + Some("bscscan.com") => Source::etherscan( + url.path() + .rsplit('/') + .next() + .ok_or_else(|| eyre!("HTTP URL does not have a path"))?, + ), Some("etherscan.io") => Source::etherscan( url.path() .rsplit('/') @@ -119,6 +133,7 @@ impl Source { ), _ => Ok(Source::Http(url)), }, + "bscscan" => Source::bscscan(url.path()), "etherscan" => Source::etherscan(url.path()), "polygonscan" => Source::polygonscan(url.path()), "snowtrace" => Source::snowtrace(url.path()), @@ -140,6 +155,16 @@ impl Source { Ok(Source::Http(Url::parse(url.as_ref())?)) } + /// Creates an Bscscan source from an address string. + pub fn bscscan(address: S) -> Result + where + S: AsRef, + { + let address = + util::parse_address(address).context("failed to parse address for Bscscan source")?; + Ok(Source::Bscscan(address)) + } + /// Creates an Etherscan source from an address string. pub fn etherscan(address: S) -> Result where @@ -187,6 +212,7 @@ impl Source { match self { Source::Local(path) => get_local_contract(path), Source::Http(_) => panic!("Http abi location are not supported for wasm"), + Source::Bscscan(_) => panic!("Bscscan abi location are not supported for wasm"), Source::Etherscan(_) => panic!("Etherscan abi location are not supported for wasm"), Source::Polygonscan(_) => panic!("Polygonscan abi location are not supported for wasm"), Source::Snowtrace(_) => panic!("Snowtrace abi location are not supported for wasm"), @@ -197,6 +223,7 @@ impl Source { match self { Source::Local(path) => get_local_contract(path), Source::Http(url) => get_http_contract(url), + Source::Bscscan(address) => get_etherscan_contract(*address, "bscscan.com"), Source::Etherscan(address) => get_etherscan_contract(*address, "etherscan.io"), Source::Polygonscan(address) => get_etherscan_contract(*address, "polygonscan.com"), Source::Snowtrace(address) => get_etherscan_contract(*address, "snowtrace.io"), @@ -261,6 +288,7 @@ fn get_etherscan_contract(address: Address, domain: &str) -> Result { // probably don't reference anything when deploying on other networks. let api_key = { let key_res = match domain { + "bscscan.com" => env::var("BSCSCAN_API_KEY").ok(), "etherscan.io" => env::var("ETHERSCAN_API_KEY").ok(), "polygonscan.com" => env::var("POLYGONSCAN_API_KEY").ok(), "snowtrace.io" => env::var("SNOWTRACE_API_KEY").ok(), @@ -311,6 +339,10 @@ mod tests { "https://my.domain.eth/path/to/Contract.json", Source::http("https://my.domain.eth/path/to/Contract.json").unwrap(), ), + ( + "bscscan:0x0001020304050607080910111213141516171819", + Source::bscscan("0x0001020304050607080910111213141516171819").unwrap(), + ), ( "etherscan:0x0001020304050607080910111213141516171819", Source::etherscan("0x0001020304050607080910111213141516171819").unwrap(), @@ -323,6 +355,10 @@ mod tests { "snowtrace:0x0001020304050607080910111213141516171819", Source::snowtrace("0x0001020304050607080910111213141516171819").unwrap(), ), + ( + "https://bscscan.io/address/0x0001020304050607080910111213141516171819", + Source::bscscan("0x0001020304050607080910111213141516171819").unwrap(), + ), ( "https://etherscan.io/address/0x0001020304050607080910111213141516171819", Source::etherscan("0x0001020304050607080910111213141516171819").unwrap(), diff --git a/ethers-contract/ethers-contract-derive/Cargo.toml b/ethers-contract/ethers-contract-derive/Cargo.toml index 85da947d..0191d2f5 100644 --- a/ethers-contract/ethers-contract-derive/Cargo.toml +++ b/ethers-contract/ethers-contract-derive/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "ethers-contract-derive" version = "1.0.2" +edition = "2021" +rust-version = "1.64" authors = [ "Nicholas Rodrigues Lordello ", "Georgios Konstantopoulos ", ] -edition = "2018" license = "MIT OR Apache-2.0" description = "Proc macro for type-safe bindings generation to Ethereum and Celo smart contracts" homepage = "https://docs.rs/ethers" @@ -17,7 +18,7 @@ proc-macro = true [dependencies] ethers-core = { version = "^1.0.0", path = "../../ethers-core" } -ethers-contract-abigen = { version = "^1.0.0", path = "../ethers-contract-abigen" } +ethers-contract-abigen = { version = "^1.0.0", path = "../ethers-contract-abigen", default-features = false } serde_json = "1.0.53" hex = { version = "0.4.3", default-features = false, features = ["std"] } diff --git a/ethers-contract/src/multicall/mod.rs b/ethers-contract/src/multicall/mod.rs index 8dd24542..0b5bc56f 100644 --- a/ethers-contract/src/multicall/mod.rs +++ b/ethers-contract/src/multicall/mod.rs @@ -437,6 +437,27 @@ impl Multicall { } } + /// Appends multiple `call`s to the list of calls of the Multicall instance. + /// + /// Version specific details: + /// - 1: `allow_failure` is ignored. + /// - >=2: `allow_failure` specifies whether or not this call is allowed to revert in the + /// multicall. + /// - 3: Transaction values are used when broadcasting transactions with [`send`], otherwise + /// they are always ignored. + /// + /// [`send`]: #method.send + pub fn add_calls( + &mut self, + allow_failure: bool, + calls: impl IntoIterator>, + ) -> &mut Self { + for call in calls { + self.add_call(call, allow_failure); + } + self + } + /// Appends a `call` to the list of calls of the Multicall instance for querying the block hash /// of a given block number. /// @@ -615,6 +636,45 @@ impl Multicall { Ok(data) } + /// Queries the Ethereum blockchain using `eth_call`, but via the Multicall contract, assuming + /// that every call returns same data type. + /// + /// Note: this method _does not_ send a transaction from your account. + /// + /// # Errors + /// + /// Returns a [`MulticallError`] if there are any errors in the RPC call or while detokenizing + /// the tokens back to the expected return type. + /// + /// # Examples + /// + /// The return type must be annotated while calling this method: + /// + /// ```no_run + /// # async fn foo() -> Result<(), Box> { + /// # use ethers_core::types::{U256, Address}; + /// # use ethers_providers::{Provider, Http}; + /// # use ethers_contract::Multicall; + /// # use std::convert::TryFrom; + /// # + /// # let client = Provider::::try_from("http://localhost:8545")?; + /// # + /// # let multicall = Multicall::new(client, None).await?; + /// // If the all Solidity function calls `returns (uint256)`: + /// let result: Vec = multicall.call_array().await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn call_array(&self) -> Result, M> { + let tokens = self.call_raw().await?; + let res: std::result::Result, ContractError> = tokens + .into_iter() + .map(|token| D::from_tokens(vec![token]).map_err(ContractError::DetokenizationError)) + .collect(); + + Ok(res?) + } + /// Queries the Ethereum blockchain using `eth_call`, but via the Multicall contract and /// without detokenization. /// diff --git a/ethers-contract/tests/it/contract.rs b/ethers-contract/tests/it/contract.rs index e15005aa..8fdd0469 100644 --- a/ethers-contract/tests/it/contract.rs +++ b/ethers-contract/tests/it/contract.rs @@ -15,7 +15,7 @@ mod eth_tests { use ethers_derive_eip712::*; use ethers_providers::{Http, Middleware, PendingTransaction, Provider, StreamExt}; use ethers_signers::{LocalWallet, Signer}; - use std::{convert::TryFrom, sync::Arc, time::Duration}; + use std::{convert::TryFrom, iter::FromIterator, sync::Arc, time::Duration}; #[tokio::test] async fn deploy_and_call_contract() { @@ -517,10 +517,26 @@ mod eth_tests { .add_get_eth_balance(addrs[5], false) .add_get_eth_balance(addrs[6], false); + let valid_balances = [ + U256::from(10_000_000_000_000_000_000_000u128), + U256::from(10_000_000_000_000_000_000_000u128), + U256::from(10_000_000_000_000_000_000_000u128), + ]; + let balances: (U256, U256, U256) = multicall.call().await.unwrap(); - assert_eq!(balances.0, U256::from(10_000_000_000_000_000_000_000u128)); - assert_eq!(balances.1, U256::from(10_000_000_000_000_000_000_000u128)); - assert_eq!(balances.2, U256::from(10_000_000_000_000_000_000_000u128)); + assert_eq!(balances.0, valid_balances[0]); + assert_eq!(balances.1, valid_balances[1]); + assert_eq!(balances.2, valid_balances[2]); + + // call_array + multicall + .clear_calls() + .add_get_eth_balance(addrs[4], false) + .add_get_eth_balance(addrs[5], false) + .add_get_eth_balance(addrs[6], false); + + let balances: Vec = multicall.call_array().await.unwrap(); + assert_eq!(balances, Vec::from_iter(valid_balances.iter().copied())); // clear multicall so we can test `call_raw` w/ >16 calls multicall.clear_calls(); @@ -536,10 +552,11 @@ mod eth_tests { .unwrap(); // build up a list of calls greater than the 16 max restriction - for i in 0..=16 { - let call = simple_contract.method::<_, String>("getValue", ()).unwrap(); - multicall.add_call(call, false); - } + multicall.add_calls( + false, + std::iter::repeat(simple_contract.method::<_, String>("getValue", ()).unwrap()) + .take(17), // .collect(), + ); // must use `call_raw` as `.calls` > 16 let tokens = multicall.call_raw().await.unwrap(); diff --git a/ethers-core/Cargo.toml b/ethers-core/Cargo.toml index 9dd1c927..8aa21a6f 100644 --- a/ethers-core/Cargo.toml +++ b/ethers-core/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "ethers-core" -license = "MIT OR Apache-2.0" version = "1.0.2" +edition = "2021" rust-version = "1.64" authors = ["Georgios Konstantopoulos "] -edition = "2018" +license = "MIT OR Apache-2.0" description = "Core structures for the ethers-rs crate" homepage = "https://docs.rs/ethers" repository = "https://github.com/gakonst/ethers-rs" @@ -31,7 +31,7 @@ serde_json = { version = "1.0.64", default-features = false, features = ["arbitr thiserror = { version = "1.0", default-features = false } bytes = { version = "1.3.0", features = ["serde"] } hex = { version = "0.4.3", default-features = false, features = ["std"] } -once_cell = { version = "1.16.0", optional = true } +once_cell = { version = "1.17.0", optional = true } unicode-xid = "0.2.4" strum = { version = "0.24", features = ["derive"] } @@ -40,14 +40,15 @@ cargo_metadata = { version = "0.15.2", optional = true } # eip712 feature enabled dependencies convert_case = { version = "0.6.0", optional = true } -syn = { version = "1.0.105", optional = true } -proc-macro2 = { version = "1.0.47", optional = true } +syn = { version = "1.0.107", optional = true } +proc-macro2 = { version = "1.0.49", optional = true } +num_enum = "0.5.7" [dev-dependencies] tempfile = { version = "3.3.0", default-features = false } serde_json = { version = "1.0.64", default-features = false } bincode = { version = "1.3.3", default-features = false } -once_cell = { version = "1.16.0" } +once_cell = { version = "1.17.0" } hex-literal = "0.3.4" rand = "0.8.5" diff --git a/ethers-core/ethers-derive-eip712/Cargo.toml b/ethers-core/ethers-derive-eip712/Cargo.toml index 305bfcfd..61073774 100644 --- a/ethers-core/ethers-derive-eip712/Cargo.toml +++ b/ethers-core/ethers-derive-eip712/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "ethers-derive-eip712" version = "1.0.2" -edition = "2018" +edition = "2021" +rust-version = "1.64" description = "Custom derive macro for EIP-712 typed data" license = "MIT OR Apache-2.0" @@ -11,7 +12,10 @@ proc-macro = true [dependencies] quote = "1.0.9" syn = "1.0.77" -ethers-core = { version = "^1.0.0", path = "../", default-features = false, features = ["eip712", "macros"] } +ethers-core = { version = "^1.0.0", path = "../", default-features = false, features = [ + "eip712", + "macros", +] } hex = "0.4.3" serde_json = "1.0.68" diff --git a/ethers-core/src/macros/ethers_crate.rs b/ethers-core/src/macros/ethers_crate.rs index 78bc38d8..2c33eb5c 100644 --- a/ethers-core/src/macros/ethers_crate.rs +++ b/ethers-core/src/macros/ethers_crate.rs @@ -63,7 +63,7 @@ pub fn determine_ethers_crates() -> (&'static str, &'static str, &'static str) { let needs_lock_file_cleanup = !std::path::Path::new(&lock_file).exists(); let res = MetadataCommand::new() - .manifest_path(&format!("{manifest_dir}/Cargo.toml")) + .manifest_path(format!("{manifest_dir}/Cargo.toml")) .exec() .ok() .and_then(|metadata| { diff --git a/ethers-core/src/types/chain.rs b/ethers-core/src/types/chain.rs index 70dd6a10..11417376 100644 --- a/ethers-core/src/types/chain.rs +++ b/ethers-core/src/types/chain.rs @@ -1,138 +1,160 @@ -use super::U256; +use super::{U128, U256, U512, U64}; +use num_enum::{TryFromPrimitive, TryFromPrimitiveError}; use serde::{Deserialize, Serialize, Serializer}; use std::{ convert::{TryFrom, TryInto}, fmt, - str::FromStr, time::Duration, }; -use strum::EnumVariantNames; -use thiserror::Error; - -#[derive(Debug, Clone, Error)] -#[error("Failed to parse chain: {0}")] -pub struct ParseChainError(String); +use strum::{AsRefStr, EnumString, EnumVariantNames}; // When adding a new chain: // 1. add new variant to the Chain enum; -// 2. update Display/FromStr impl; -// 3. add etherscan_keys if supported. +// 2. add extra information in the last `impl` block (explorer URLs, block time) when applicable; +// 3. (optional) add aliases: `#[strum(serialize = "main", serialize = "alias", ...)]`; +// "main" must be present and will be used in `Display`, `Serialize` and `FromStr`, +// while the aliases will be added only to `FromStr`. -/// Enum for all known chains. -#[repr(u64)] -#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, Deserialize, EnumVariantNames)] +/// An Ethereum EIP-155 chain. +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + AsRefStr, // also for fmt::Display and serde::Serialize + EnumVariantNames, // Self::VARIANTS + EnumString, // FromStr, TryFrom<&str> + TryFromPrimitive, // TryFrom + Deserialize, +)] #[serde(rename_all = "snake_case")] #[strum(serialize_all = "kebab-case")] +#[repr(u64)] pub enum Chain { - #[default] Mainnet = 1, Morden = 2, Ropsten = 3, Rinkeby = 4, Goerli = 5, - Optimism = 10, - Cronos = 25, - Rsk = 30, Kovan = 42, - #[strum(serialize = "bsc")] - BinanceSmartChain = 56, + Sepolia = 11155111, + + Optimism = 10, OptimismKovan = 69, - Sokol = 77, - #[strum(serialize = "bsc-testnet")] - BinanceSmartChainTestnet = 97, - Poa = 99, - #[strum(serialize = "gnosis")] - XDai = 100, - Polygon = 137, - Fantom = 250, - CronosTestnet = 338, OptimismGoerli = 420, - MoonbeamDev = 1281, - Moonbeam = 1284, - Moonriver = 1285, - Moonbase = 1287, - Dev = 1337, - FantomTestnet = 4002, - EvmosTestnet = 9000, - Evmos = 9001, - Chiado = 10200, - Oasis = 26863, - AnvilHardhat = 31337, + Arbitrum = 42161, - EmeraldTestnet = 42261, - Emerald = 42262, - AvalancheFuji = 43113, - Avalanche = 43114, - PolygonMumbai = 80001, ArbitrumTestnet = 421611, ArbitrumGoerli = 421613, - Sepolia = 11155111, - Aurora = 1313161554, - AuroraTestnet = 1313161555, + + Cronos = 25, + CronosTestnet = 338, + + Rsk = 30, + + #[strum(serialize = "bsc")] + BinanceSmartChain = 56, + #[strum(serialize = "bsc-testnet")] + BinanceSmartChainTestnet = 97, + + Poa = 99, + Sokol = 77, + + #[strum(serialize = "gnosis", serialize = "xdai", serialize = "gnosis-chain")] + XDai = 100, + + Polygon = 137, + #[strum(serialize = "mumbai", serialize = "polygon-mumbai")] + PolygonMumbai = 80001, + + Fantom = 250, + FantomTestnet = 4002, + + Moonbeam = 1284, + MoonbeamDev = 1281, + + Moonriver = 1285, + + Moonbase = 1287, + + #[strum(serialize = "dev")] + Dev = 1337, + #[strum(serialize = "anvil-hardhat", serialize = "anvil", serialize = "hardhat")] + AnvilHardhat = 31337, + + Evmos = 9001, + EvmosTestnet = 9000, + + Chiado = 10200, + + Oasis = 26863, + + Emerald = 42262, + EmeraldTestnet = 42261, + + Avalanche = 43114, + #[strum(serialize = "fuji", serialize = "avalanche-fuji")] + AvalancheFuji = 43113, + Celo = 42220, CeloAlfajores = 44787, CeloBaklava = 62320, + + Aurora = 1313161554, + AuroraTestnet = 1313161555, } // === impl Chain === -impl fmt::Display for Chain { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let chain = match self { - Chain::Mainnet => "mainnet", - Chain::Morden => "morden", - Chain::Ropsten => "ropsten", - Chain::Rinkeby => "rinkeby", - Chain::Goerli => "goerli", - Chain::Kovan => "kovan", - Chain::XDai => "gnosis", - Chain::Chiado => "chiado", - Chain::Polygon => "polygon", - Chain::PolygonMumbai => "mumbai", - Chain::Avalanche => "avalanche", - Chain::AvalancheFuji => "fuji", - Chain::Sepolia => "sepolia", - Chain::Moonbeam => "moonbeam", - Chain::Moonbase => "moonbase", - Chain::MoonbeamDev => "moonbeam-dev", - Chain::Moonriver => "moonriver", - Chain::Optimism => "optimism", - Chain::OptimismGoerli => "optimism-goerli", - Chain::OptimismKovan => "optimism-kovan", - Chain::Fantom => "fantom", - Chain::Dev => "dev", - Chain::FantomTestnet => "fantom-testnet", - Chain::BinanceSmartChain => "bsc", - Chain::BinanceSmartChainTestnet => "bsc-testnet", - Chain::Arbitrum => "arbitrum", - Chain::ArbitrumTestnet => "arbitrum-testnet", - Chain::ArbitrumGoerli => "arbitrum-goerli", - Chain::Cronos => "cronos", - Chain::CronosTestnet => "cronos-testnet", - Chain::Poa => "poa", - Chain::Sokol => "sokol", - Chain::Rsk => "rsk", - Chain::Oasis => "oasis", - Chain::Emerald => "emerald", - Chain::EmeraldTestnet => "emerald-testnet", - Chain::AnvilHardhat => "anvil-hardhat", - Chain::Evmos => "evmos", - Chain::EvmosTestnet => "evmos-testnet", - Chain::Aurora => "aurora", - Chain::AuroraTestnet => "aurora-testnet", - Chain::Celo => "celo", - Chain::CeloAlfajores => "celo-alfajores", - Chain::CeloBaklava => "celo-baklava", - }; - - f.pad(chain) +// This must be implemented manually so we avoid a conflict with `TryFromPrimitive` where it treats +// the `#[default]` attribute as its own `#[num_enum(default)]` +impl Default for Chain { + fn default() -> Self { + Self::Mainnet } } -impl From for u32 { - fn from(chain: Chain) -> Self { - chain as u32 - } +macro_rules! impl_into_numeric { + ($($ty:ty)+) => {$( + impl From for $ty { + fn from(chain: Chain) -> Self { + u64::from(chain).into() + } + } + )+}; +} + +macro_rules! impl_try_from_numeric { + ($($native:ty)+ ; $($primitive:ty)*) => { + $( + impl TryFrom<$native> for Chain { + type Error = TryFromPrimitiveError; + + fn try_from(value: $native) -> Result { + (value as u64).try_into() + } + } + )+ + + $( + impl TryFrom<$primitive> for Chain { + type Error = TryFromPrimitiveError; + + fn try_from(value: $primitive) -> Result { + if value.bits() > 64 { + // `TryFromPrimitiveError` only has a `number` field which has the same type + // as the `#[repr(_)]` attribute on the enum. + return Err(TryFromPrimitiveError { number: value.low_u64() }) + } + value.low_u64().try_into() + } + } + )* + }; } impl From for u64 { @@ -141,134 +163,21 @@ impl From for u64 { } } -impl From for U256 { - fn from(chain: Chain) -> Self { - u64::from(chain).into() +impl_into_numeric!(u128 U64 U128 U256 U512); + +impl TryFrom for Chain { + type Error = TryFromPrimitiveError; + + fn try_from(value: U64) -> Result { + value.low_u64().try_into() } } -impl TryFrom for Chain { - type Error = ParseChainError; +impl_try_from_numeric!(u8 u16 u32 usize; U128 U256 U512); - fn try_from(chain: u32) -> Result { - (chain as u64).try_into() - } -} - -impl TryFrom for Chain { - type Error = ParseChainError; - - fn try_from(chain: u64) -> Result { - Ok(match chain { - 1 => Chain::Mainnet, - 2 => Chain::Morden, - 3 => Chain::Ropsten, - 4 => Chain::Rinkeby, - 5 => Chain::Goerli, - 42 => Chain::Kovan, - 100 => Chain::XDai, - 10200 => Chain::Chiado, - 137 => Chain::Polygon, - 1337 => Chain::Dev, - 31337 => Chain::AnvilHardhat, - 250 => Chain::Fantom, - 4002 => Chain::FantomTestnet, - 80001 => Chain::PolygonMumbai, - 43114 => Chain::Avalanche, - 43113 => Chain::AvalancheFuji, - 11155111 => Chain::Sepolia, - 1284 => Chain::Moonbeam, - 1287 => Chain::Moonbase, - 1281 => Chain::MoonbeamDev, - 1285 => Chain::Moonriver, - 10 => Chain::Optimism, - 420 => Chain::OptimismGoerli, - 69 => Chain::OptimismKovan, - 56 => Chain::BinanceSmartChain, - 97 => Chain::BinanceSmartChainTestnet, - 42161 => Chain::Arbitrum, - 421611 => Chain::ArbitrumTestnet, - 421613 => Chain::ArbitrumGoerli, - 25 => Chain::Cronos, - 338 => Chain::CronosTestnet, - 99 => Chain::Poa, - 77 => Chain::Sokol, - 30 => Chain::Rsk, - 26863 => Chain::Oasis, - 42262 => Chain::Emerald, - 42261 => Chain::EmeraldTestnet, - 9001 => Chain::Evmos, - 9000 => Chain::EvmosTestnet, - 1313161554 => Chain::Aurora, - 1313161555 => Chain::AuroraTestnet, - 42220 => Chain::Celo, - 44787 => Chain::CeloAlfajores, - 62320 => Chain::CeloBaklava, - _ => return Err(ParseChainError(chain.to_string())), - }) - } -} - -impl TryFrom for Chain { - type Error = ParseChainError; - - fn try_from(chain: U256) -> Result { - if chain.bits() > 64 { - return Err(ParseChainError(chain.to_string())) - } - chain.as_u64().try_into() - } -} - -impl FromStr for Chain { - type Err = ParseChainError; - - fn from_str(chain: &str) -> Result { - Ok(match chain { - "mainnet" => Chain::Mainnet, - "morden" => Chain::Morden, - "ropsten" => Chain::Ropsten, - "rinkeby" => Chain::Rinkeby, - "goerli" => Chain::Goerli, - "kovan" => Chain::Kovan, - "xdai" | "gnosis" | "gnosis-chain" => Chain::XDai, - "chiado" => Chain::Chiado, - "polygon" => Chain::Polygon, - "mumbai" | "polygon-mumbai" => Chain::PolygonMumbai, - "avalanche" => Chain::Avalanche, - "fuji" | "avalanche-fuji" => Chain::AvalancheFuji, - "sepolia" => Chain::Sepolia, - "moonbeam" => Chain::Moonbeam, - "moonbase" => Chain::Moonbase, - "moonbeam-dev" => Chain::MoonbeamDev, - "moonriver" => Chain::Moonriver, - "optimism" => Chain::Optimism, - "optimism-goerli" => Chain::OptimismGoerli, - "optimism-kovan" => Chain::OptimismKovan, - "fantom" => Chain::Fantom, - "fantom-testnet" => Chain::FantomTestnet, - "dev" => Chain::Dev, - "anvil" | "hardhat" | "anvil-hardhat" => Chain::AnvilHardhat, - "bsc" => Chain::BinanceSmartChain, - "bsc-testnet" => Chain::BinanceSmartChainTestnet, - "arbitrum" => Chain::Arbitrum, - "arbitrum-testnet" => Chain::ArbitrumTestnet, - "arbitrum-goerli" => Chain::ArbitrumGoerli, - "cronos" => Chain::Cronos, - "cronos-testnet" => Chain::CronosTestnet, - "poa" => Chain::Poa, - "sokol" => Chain::Sokol, - "rsk" => Chain::Rsk, - "oasis" => Chain::Oasis, - "emerald" => Chain::Emerald, - "emerald-testnet" => Chain::EmeraldTestnet, - "aurora" => Chain::Aurora, - "aurora-testnet" => Chain::AuroraTestnet, - "celo" => Chain::Celo, - "celo-alfajores" => Chain::CeloAlfajores, - "celo-baklava" => Chain::CeloBaklava, - _ => return Err(ParseChainError(chain.to_owned())), - }) +impl fmt::Display for Chain { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad(self.as_ref()) } } @@ -277,163 +186,135 @@ impl Serialize for Chain { where S: Serializer, { - s.serialize_str(self.to_string().as_ref()) + s.serialize_str(self.as_ref()) } } +// NB: all utility functions *should* be explicitly exhaustive (not use `_` matcher) so we don't +// forget to update them when adding a new `Chain` variant. impl Chain { - /// The blocktime varies from chain to chain. + /// Returns the chain's average blocktime, if applicable. /// /// It can be beneficial to know the average blocktime to adjust the polling of an HTTP provider /// for example. /// - /// **Note:** this will not return the accurate average depending on the time but is rather a - /// sensible default derived from blocktime charts like - /// - pub fn average_blocktime_hint(&self) -> Option { + /// **Note:** this is not an accurate average, but is rather a sensible default derived from + /// blocktime charts such as [Etherscan's](https://etherscan.com/chart/blocktime) + /// or [Polygonscan's](https://polygonscan.com/chart/blocktime). + pub const fn average_blocktime_hint(&self) -> Option { + use Chain::*; + let ms = match self { - Chain::Arbitrum | Chain::ArbitrumTestnet | Chain::ArbitrumGoerli => 1_300, - Chain::Mainnet | Chain::Optimism => 13_000, - Chain::Polygon | Chain::PolygonMumbai => 2_100, - Chain::Moonbeam | Chain::Moonriver => 12_500, - Chain::BinanceSmartChain | Chain::BinanceSmartChainTestnet => 3_000, - Chain::Avalanche | Chain::AvalancheFuji => 2_000, - Chain::Fantom | Chain::FantomTestnet => 1_200, - Chain::Cronos | Chain::CronosTestnet => 5_700, - Chain::Evmos | Chain::EvmosTestnet => 1_900, - Chain::Aurora | Chain::AuroraTestnet => 1_100, - Chain::Oasis => 5_500, - Chain::Emerald => 6_000, - Chain::Dev | Chain::AnvilHardhat => 200, - Chain::Celo | Chain::CeloAlfajores | Chain::CeloBaklava => 5_000, + Arbitrum | ArbitrumTestnet | ArbitrumGoerli => 1_300, + Mainnet | Optimism => 13_000, + Polygon | PolygonMumbai => 2_100, + Moonbeam | Moonriver => 12_500, + BinanceSmartChain | BinanceSmartChainTestnet => 3_000, + Avalanche | AvalancheFuji => 2_000, + Fantom | FantomTestnet => 1_200, + Cronos | CronosTestnet => 5_700, + Evmos | EvmosTestnet => 1_900, + Aurora | AuroraTestnet => 1_100, + Oasis => 5_500, + Emerald => 6_000, + Dev | AnvilHardhat => 200, + Celo | CeloAlfajores | CeloBaklava => 5_000, // Explictly handle all network to make it easier not to forget this match when new // networks are added. - Chain::Morden | - Chain::Ropsten | - Chain::Rinkeby | - Chain::Goerli | - Chain::Kovan | - Chain::XDai | - Chain::Chiado | - Chain::Sepolia | - Chain::Moonbase | - Chain::MoonbeamDev | - Chain::OptimismGoerli | - Chain::OptimismKovan | - Chain::Poa | - Chain::Sokol | - Chain::Rsk | - Chain::EmeraldTestnet => return None, + Morden | Ropsten | Rinkeby | Goerli | Kovan | XDai | Chiado | Sepolia | Moonbase | + MoonbeamDev | OptimismGoerli | OptimismKovan | Poa | Sokol | Rsk | EmeraldTestnet => { + return None + } }; Some(Duration::from_millis(ms)) } - /// Returns the corresponding etherscan URLs + /// Returns the chain's blockchain explorer and its API (Etherscan and Etherscan-like) URLs. /// /// Returns `(API URL, BASE_URL)`, like `("https://api(-chain).etherscan.io/api", "https://etherscan.io")` - pub fn etherscan_urls(&self) -> Option<(&'static str, &'static str)> { + pub const fn etherscan_urls(&self) -> Option<(&'static str, &'static str)> { + use Chain::*; + let urls = match self { - Chain::Mainnet => ("https://api.etherscan.io/api", "https://etherscan.io"), - Chain::Ropsten => { - ("https://api-ropsten.etherscan.io/api", "https://ropsten.etherscan.io") - } - Chain::Kovan => ("https://api-kovan.etherscan.io/api", "https://kovan.etherscan.io"), - Chain::Rinkeby => { - ("https://api-rinkeby.etherscan.io/api", "https://rinkeby.etherscan.io") - } - Chain::Goerli => ("https://api-goerli.etherscan.io/api", "https://goerli.etherscan.io"), - Chain::Sepolia => { - ("https://api-sepolia.etherscan.io/api", "https://sepolia.etherscan.io") - } - Chain::Polygon => ("https://api.polygonscan.com/api", "https://polygonscan.com"), - Chain::PolygonMumbai => { + Mainnet => ("https://api.etherscan.io/api", "https://etherscan.io"), + Ropsten => ("https://api-ropsten.etherscan.io/api", "https://ropsten.etherscan.io"), + Kovan => ("https://api-kovan.etherscan.io/api", "https://kovan.etherscan.io"), + Rinkeby => ("https://api-rinkeby.etherscan.io/api", "https://rinkeby.etherscan.io"), + Goerli => ("https://api-goerli.etherscan.io/api", "https://goerli.etherscan.io"), + Sepolia => ("https://api-sepolia.etherscan.io/api", "https://sepolia.etherscan.io"), + Polygon => ("https://api.polygonscan.com/api", "https://polygonscan.com"), + PolygonMumbai => { ("https://api-testnet.polygonscan.com/api", "https://mumbai.polygonscan.com") } - Chain::Avalanche => ("https://api.snowtrace.io/api", "https://snowtrace.io"), - Chain::AvalancheFuji => { + Avalanche => ("https://api.snowtrace.io/api", "https://snowtrace.io"), + AvalancheFuji => { ("https://api-testnet.snowtrace.io/api", "https://testnet.snowtrace.io") } - Chain::Optimism => { + Optimism => { ("https://api-optimistic.etherscan.io/api", "https://optimistic.etherscan.io") } - Chain::OptimismGoerli => ( + OptimismGoerli => ( "https://api-goerli-optimistic.etherscan.io/api", "https://goerli-optimism.etherscan.io", ), - Chain::OptimismKovan => ( + OptimismKovan => ( "https://api-kovan-optimistic.etherscan.io/api", "https://kovan-optimistic.etherscan.io", ), - Chain::Fantom => ("https://api.ftmscan.com/api", "https://ftmscan.com"), - Chain::FantomTestnet => { - ("https://api-testnet.ftmscan.com/api", "https://testnet.ftmscan.com") - } - Chain::BinanceSmartChain => ("https://api.bscscan.com/api", "https://bscscan.com"), - Chain::BinanceSmartChainTestnet => { + Fantom => ("https://api.ftmscan.com/api", "https://ftmscan.com"), + FantomTestnet => ("https://api-testnet.ftmscan.com/api", "https://testnet.ftmscan.com"), + BinanceSmartChain => ("https://api.bscscan.com/api", "https://bscscan.com"), + BinanceSmartChainTestnet => { ("https://api-testnet.bscscan.com/api", "https://testnet.bscscan.com") } - Chain::Arbitrum => ("https://api.arbiscan.io/api", "https://arbiscan.io"), - Chain::ArbitrumTestnet => { + Arbitrum => ("https://api.arbiscan.io/api", "https://arbiscan.io"), + ArbitrumTestnet => { ("https://api-testnet.arbiscan.io/api", "https://testnet.arbiscan.io") } - Chain::ArbitrumGoerli => ( + ArbitrumGoerli => ( "https://goerli-rollup-explorer.arbitrum.io/api", "https://goerli-rollup-explorer.arbitrum.io", ), - Chain::Cronos => ("https://api.cronoscan.com/api", "https://cronoscan.com"), - Chain::CronosTestnet => { + Cronos => ("https://api.cronoscan.com/api", "https://cronoscan.com"), + CronosTestnet => { ("https://api-testnet.cronoscan.com/api", "https://testnet.cronoscan.com") } - Chain::Moonbeam => { - ("https://api-moonbeam.moonscan.io/api", "https://moonbeam.moonscan.io/") - } - Chain::Moonbase => { - ("https://api-moonbase.moonscan.io/api", "https://moonbase.moonscan.io/") - } - Chain::Moonriver => { - ("https://api-moonriver.moonscan.io/api", "https://moonriver.moonscan.io") - } + Moonbeam => ("https://api-moonbeam.moonscan.io/api", "https://moonbeam.moonscan.io/"), + Moonbase => ("https://api-moonbase.moonscan.io/api", "https://moonbase.moonscan.io/"), + Moonriver => ("https://api-moonriver.moonscan.io/api", "https://moonriver.moonscan.io"), // blockscout API is etherscan compatible - Chain::XDai => { + XDai => { ("https://blockscout.com/xdai/mainnet/api", "https://blockscout.com/xdai/mainnet") } - Chain::Chiado => { + Chiado => { ("https://blockscout.chiadochain.net/api", "https://blockscout.chiadochain.net") } - Chain::Sokol => { - ("https://blockscout.com/poa/sokol/api", "https://blockscout.com/poa/sokol") - } - Chain::Poa => { - ("https://blockscout.com/poa/core/api", "https://blockscout.com/poa/core") - } - Chain::Rsk => { - ("https://blockscout.com/rsk/mainnet/api", "https://blockscout.com/rsk/mainnet") - } - Chain::Oasis => ("https://scan.oasischain.io/api", "https://scan.oasischain.io/"), - Chain::Emerald => { + Sokol => ("https://blockscout.com/poa/sokol/api", "https://blockscout.com/poa/sokol"), + Poa => ("https://blockscout.com/poa/core/api", "https://blockscout.com/poa/core"), + Rsk => ("https://blockscout.com/rsk/mainnet/api", "https://blockscout.com/rsk/mainnet"), + Oasis => ("https://scan.oasischain.io/api", "https://scan.oasischain.io/"), + Emerald => { ("https://explorer.emerald.oasis.dev/api", "https://explorer.emerald.oasis.dev/") } - Chain::EmeraldTestnet => ( + EmeraldTestnet => ( "https://testnet.explorer.emerald.oasis.dev/api", "https://testnet.explorer.emerald.oasis.dev/", ), - Chain::Aurora => ("https://api.aurorascan.dev/api", "https://aurorascan.dev"), - Chain::AuroraTestnet => { + Aurora => ("https://api.aurorascan.dev/api", "https://aurorascan.dev"), + AuroraTestnet => { ("https://testnet.aurorascan.dev/api", "https://testnet.aurorascan.dev") } - Chain::Evmos => ("https://evm.evmos.org/api", "https://evm.evmos.org/"), - Chain::EvmosTestnet => ("https://evm.evmos.dev/api", "https://evm.evmos.dev/"), - Chain::Celo => { - ("https://explorer.celo.org/mainnet", "https://explorer.celo.org/mainnet/api") - } - Chain::CeloAlfajores => { + Evmos => ("https://evm.evmos.org/api", "https://evm.evmos.org/"), + EvmosTestnet => ("https://evm.evmos.dev/api", "https://evm.evmos.dev/"), + Celo => ("https://explorer.celo.org/mainnet", "https://explorer.celo.org/mainnet/api"), + CeloAlfajores => { ("https://explorer.celo.org/alfajores", "https://explorer.celo.org/alfajores/api") } - Chain::CeloBaklava => { + CeloBaklava => { ("https://explorer.celo.org/baklava", "https://explorer.celo.org/baklava/api") } - Chain::AnvilHardhat | Chain::Dev | Chain::Morden | Chain::MoonbeamDev => { + AnvilHardhat | Dev | Morden | MoonbeamDev => { // this is explicitly exhaustive so we don't forget to add new urls when adding a // new chain return None @@ -443,34 +324,49 @@ impl Chain { Some(urls) } - /// Helper function for checking if a chainid corresponds to a legacy chainid - /// without eip1559 - pub fn is_legacy(&self) -> bool { - // TODO: Add other chains which do not support EIP1559. - matches!( - self, - Chain::Optimism | - Chain::OptimismGoerli | - Chain::OptimismKovan | - Chain::Fantom | - Chain::FantomTestnet | - Chain::BinanceSmartChain | - Chain::BinanceSmartChainTestnet | - Chain::Arbitrum | - Chain::ArbitrumTestnet | - Chain::ArbitrumGoerli | - Chain::Rsk | - Chain::Oasis | - Chain::Emerald | - Chain::EmeraldTestnet | - Chain::Celo | - Chain::CeloAlfajores | - Chain::CeloBaklava, - ) + /// Returns whether the chain implements EIP-1559 (with the type 2 EIP-2718 transaction type). + pub const fn is_legacy(&self) -> bool { + use Chain::*; + + match self { + // Known legacy chains / non EIP-1559 compliant + Optimism | + OptimismGoerli | + OptimismKovan | + Fantom | + FantomTestnet | + BinanceSmartChain | + BinanceSmartChainTestnet | + Arbitrum | + ArbitrumTestnet | + ArbitrumGoerli | + Rsk | + Oasis | + Emerald | + EmeraldTestnet | + Celo | + CeloAlfajores | + CeloBaklava => true, + + // Known EIP-1559 chains + Mainnet | Goerli | Sepolia | Polygon | PolygonMumbai | Avalanche | AvalancheFuji => { + false + } + + // Unknown / not applicable, default to false for backwards compatibility + Dev | AnvilHardhat | Morden | Ropsten | Rinkeby | Cronos | CronosTestnet | Kovan | + Sokol | Poa | XDai | Moonbeam | MoonbeamDev | Moonriver | Moonbase | Evmos | + EvmosTestnet | Chiado | Aurora | AuroraTestnet => false, + } } } -#[test] -fn test_default_chain() { - assert_eq!(serde_json::to_string(&Chain::Mainnet).unwrap(), "\"mainnet\""); +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_default_chain() { + assert_eq!(serde_json::to_string(&Chain::default()).unwrap(), "\"mainnet\""); + } } diff --git a/ethers-core/src/types/trace/geth.rs b/ethers-core/src/types/trace/geth.rs index d18632c9..c346a299 100644 --- a/ethers-core/src/types/trace/geth.rs +++ b/ethers-core/src/types/trace/geth.rs @@ -1,4 +1,7 @@ -use crate::types::{Bytes, H256, U256}; +use crate::{ + types::{Bytes, H256, U256}, + utils::from_int_or_hex, +}; use serde::{Deserialize, Serialize, Serializer}; use std::collections::BTreeMap; @@ -6,7 +9,8 @@ use std::collections::BTreeMap; #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct GethTrace { pub failed: bool, - pub gas: u64, + #[serde(deserialize_with = "from_int_or_hex")] + pub gas: U256, #[serde(serialize_with = "serialize_bytes", rename = "returnValue")] pub return_value: Bytes, #[serde(rename = "structLogs")] @@ -35,6 +39,16 @@ pub struct StructLog { pub storage: Option>, } +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +/// Available built-in tracers +/// +/// See +pub enum GethDebugTracerType { + /// callTracer (native) + #[serde(rename = "callTracer")] + CallTracer, +} + /// Bindings for additional `debug_traceTransaction` options /// /// See @@ -50,11 +64,22 @@ pub struct GethDebugTracingOptions { #[serde(default, skip_serializing_if = "Option::is_none")] pub enable_return_data: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub tracer: Option, + pub tracer: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub timeout: Option, } +/// Bindings for additional `debug_traceCall` options +/// +/// See +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct GethDebugTracingCallOptions { + #[serde(flatten)] + pub tracing_options: GethDebugTracingOptions, + // TODO: Add stateoverrides and blockoverrides options +} + fn serialize_bytes(x: T, s: S) -> Result where S: Serializer, diff --git a/ethers-core/src/types/transaction/eip1559.rs b/ethers-core/src/types/transaction/eip1559.rs index ba080e19..ba937fe6 100644 --- a/ethers-core/src/types/transaction/eip1559.rs +++ b/ethers-core/src/types/transaction/eip1559.rs @@ -208,7 +208,7 @@ impl Eip1559TransactionRequest { *offset += 1; tx.gas = Some(rlp.val_at(*offset)?); *offset += 1; - tx.to = decode_to(rlp, offset)?; + tx.to = decode_to(rlp, offset)?.map(NameOrAddress::Address); tx.value = Some(rlp.val_at(*offset)?); *offset += 1; let data = rlp::Rlp::new(rlp.at(*offset)?.as_raw()).data()?; diff --git a/ethers-core/src/types/transaction/mod.rs b/ethers-core/src/types/transaction/mod.rs index efe70780..816f4539 100644 --- a/ethers-core/src/types/transaction/mod.rs +++ b/ethers-core/src/types/transaction/mod.rs @@ -75,7 +75,7 @@ fn decode_signature( fn decode_to( rlp: &rlp::Rlp, offset: &mut usize, -) -> Result, rlp::DecoderError> { +) -> Result, rlp::DecoderError> { let to = { let to = rlp.at(*offset)?; if to.is_empty() { diff --git a/ethers-core/src/types/transaction/request.rs b/ethers-core/src/types/transaction/request.rs index f8be2627..d31d91bf 100644 --- a/ethers-core/src/types/transaction/request.rs +++ b/ethers-core/src/types/transaction/request.rs @@ -233,7 +233,7 @@ impl TransactionRequest { *offset += 1; } - txn.to = decode_to(rlp, offset)?; + txn.to = decode_to(rlp, offset)?.map(NameOrAddress::Address); txn.value = Some(rlp.at(*offset)?.as_val()?); *offset += 1; diff --git a/ethers-core/src/types/transaction/response.rs b/ethers-core/src/types/transaction/response.rs index 314caf0b..b29d6f99 100644 --- a/ethers-core/src/types/transaction/response.rs +++ b/ethers-core/src/types/transaction/response.rs @@ -1,7 +1,7 @@ //! Transaction types use super::{ - decode_signature, eip2718::TypedTransaction, eip2930::AccessList, normalize_v, rlp_opt, - rlp_opt_list, + decode_signature, decode_to, eip2718::TypedTransaction, eip2930::AccessList, normalize_v, + rlp_opt, rlp_opt_list, }; use crate::{ types::{ @@ -248,8 +248,7 @@ impl Transaction { *offset += 1; self.gas = rlp.val_at(*offset)?; *offset += 1; - self.to = Some(rlp.val_at(*offset)?); - *offset += 1; + self.to = decode_to(rlp, offset)?; self.value = rlp.val_at(*offset)?; *offset += 1; let input = rlp::Rlp::new(rlp.at(*offset)?.as_raw()).data()?; @@ -279,8 +278,7 @@ impl Transaction { #[cfg(feature = "celo")] self.decode_celo_metadata(rlp, offset)?; - self.to = Some(rlp.val_at(*offset)?); - *offset += 1; + self.to = decode_to(rlp, offset)?; self.value = rlp.val_at(*offset)?; *offset += 1; let input = rlp::Rlp::new(rlp.at(*offset)?.as_raw()).data()?; @@ -310,8 +308,7 @@ impl Transaction { #[cfg(feature = "celo")] self.decode_celo_metadata(rlp, offset)?; - self.to = Some(rlp.val_at(*offset)?); - *offset += 1; + self.to = decode_to(rlp, offset)?; self.value = rlp.val_at(*offset)?; *offset += 1; let input = rlp::Rlp::new(rlp.at(*offset)?.as_raw()).data()?; @@ -473,7 +470,7 @@ impl PartialOrd for TransactionReceipt { #[cfg(test)] #[cfg(not(feature = "celo"))] mod tests { - use rlp::Encodable; + use rlp::{Encodable, Rlp}; use crate::types::transaction::eip2930::AccessListItem; @@ -1093,4 +1090,24 @@ mod tests { let r = rlp::Rlp::new(b.as_slice()); Transaction::decode(&r).unwrap(); } + + #[test] + fn test_rlp_decoding_create_roundtrip() { + let tx = Transaction { + block_hash: None, + block_number: None, + from: Address::from_str("c26ad91f4e7a0cad84c4b9315f420ca9217e315d").unwrap(), + gas: U256::from_str_radix("0x10e2b", 16).unwrap(), + gas_price: Some(U256::from_str_radix("0x12ec276caf", 16).unwrap()), + hash: H256::from_str("929ff27a5c7833953df23103c4eb55ebdfb698678139d751c51932163877fada").unwrap(), + input: Bytes::from( + hex::decode("a9059cbb000000000000000000000000fdae129ecc2c27d166a3131098bc05d143fa258e0000000000000000000000000000000000000000000000000000000002faf080").unwrap() + ), + nonce: U256::zero(), + transaction_index: None, + value: U256::zero(), + ..Default::default() + }; + Transaction::decode(&Rlp::new(&tx.rlp())).unwrap(); + } } diff --git a/ethers-core/src/utils/geth.rs b/ethers-core/src/utils/geth.rs index 8657c888..8c13d175 100644 --- a/ethers-core/src/utils/geth.rs +++ b/ethers-core/src/utils/geth.rs @@ -164,6 +164,7 @@ impl Default for PrivateNetOptions { /// ``` #[derive(Clone, Default)] pub struct Geth { + program: Option, port: Option, authrpc_port: Option, ipc_path: Option, @@ -180,6 +181,32 @@ impl Geth { Self::default() } + /// Creates a Geth builder which will execute `geth` at the given path. + /// + /// # Example + /// + /// ``` + /// use ethers_core::utils::Geth; + /// # fn a() { + /// let geth = Geth::at("../go-ethereum/build/bin/geth").spawn(); + /// + /// println!("Geth running at `{}`", geth.endpoint()); + /// # } + /// ``` + pub fn at(path: impl Into) -> Self { + Self::new().path(path) + } + + /// Sets the `path` to the `geth` executable + /// + /// By default, it's expected that `geth` is in `$PATH`, see also + /// [`std::process::Command::new()`] + #[must_use] + pub fn path>(mut self, path: T) -> Self { + self.program = Some(path.into()); + self + } + /// Sets the port which will be used when the `geth-cli` instance is launched. #[must_use] pub fn port>(mut self, port: T) -> Self { @@ -274,9 +301,10 @@ impl Geth { /// Consumes the builder and spawns `geth` with stdout redirected /// to /dev/null. pub fn spawn(self) -> GethInstance { - let mut cmd = Command::new(GETH); + let mut cmd = + if let Some(ref prg) = self.program { Command::new(prg) } else { Command::new(GETH) }; // geth uses stderr for its logs - cmd.stderr(std::process::Stdio::piped()); + cmd.stderr(Stdio::piped()); let port = if let Some(port) = self.port { port } else { unused_port() }; let authrpc_port = if let Some(port) = self.authrpc_port { port } else { unused_port() }; diff --git a/ethers-core/src/utils/mod.rs b/ethers-core/src/utils/mod.rs index 5a2e1315..ff5ca22a 100644 --- a/ethers-core/src/utils/mod.rs +++ b/ethers-core/src/utils/mod.rs @@ -12,7 +12,7 @@ pub use geth::{Geth, GethInstance}; /// Utilities for working with a `genesis.json` and other chain config structs. mod genesis; -pub use genesis::{ChainConfig, Genesis}; +pub use genesis::{ChainConfig, CliqueConfig, EthashConfig, Genesis, GenesisAccount}; /// Utilities for launching an anvil instance #[cfg(not(target_arch = "wasm32"))] diff --git a/ethers-etherscan/Cargo.toml b/ethers-etherscan/Cargo.toml index a6f71c6b..8de6d5ac 100644 --- a/ethers-etherscan/Cargo.toml +++ b/ethers-etherscan/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "ethers-etherscan" version = "1.0.2" +edition = "2021" rust-version = "1.64" authors = [ "Matthias Seitz ", "Georgios Konstantopoulos ", ] license = "MIT OR Apache-2.0" -edition = "2018" readme = "../README.md" documentation = "https://docs.rs/ethers" repository = "https://github.com/gakonst/ethers-rs" @@ -26,7 +26,7 @@ serde_json = { version = "1.0.64", default-features = false } serde-aux = { version = "4.1.2", default-features = false } thiserror = "1.0" tracing = "0.1.37" -semver = "1.0.14" +semver = "1.0.16" [target.'cfg(target_arch = "wasm32")'.dependencies] # NOTE: this enables wasm compatibility for getrandom indirectly @@ -35,7 +35,7 @@ getrandom = { version = "0.2", features = ["js"] } [dev-dependencies] tempfile = "3.3.0" tokio = { version = "1.18", features = ["macros", "rt-multi-thread", "time"] } -serial_test = "0.9.0" +serial_test = "0.10.0" tracing-subscriber = { version = "0.3", default-features = false, features = ["env-filter", "fmt"] } ethers-solc = { version = "^1.0.0", path = "../ethers-solc", default-features = false } diff --git a/ethers-etherscan/src/contract.rs b/ethers-etherscan/src/contract.rs index ae19abf6..2b5adb23 100644 --- a/ethers-etherscan/src/contract.rs +++ b/ethers-etherscan/src/contract.rs @@ -1,6 +1,6 @@ use crate::{ source_tree::{SourceTree, SourceTreeEntry}, - utils::{deserialize_address_opt, deserialize_stringified_source_code}, + utils::{deserialize_address_opt, deserialize_source_code}, Client, EtherscanError, Response, Result, }; use ethers_core::{ @@ -107,7 +107,7 @@ impl SourceCodeMetadata { #[serde(rename_all = "PascalCase")] pub struct Metadata { /// Includes metadata for compiler settings and language. - #[serde(deserialize_with = "deserialize_stringified_source_code")] + #[serde(deserialize_with = "deserialize_source_code")] pub source_code: SourceCodeMetadata, /// The ABI of the contract. @@ -148,7 +148,11 @@ pub struct Metadata { pub proxy: u64, /// If this contract is a proxy, the address of its implementation. - #[serde(deserialize_with = "deserialize_address_opt")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "deserialize_address_opt" + )] pub implementation: Option
, /// The swarm source of the contract. diff --git a/ethers-etherscan/src/utils.rs b/ethers-etherscan/src/utils.rs index b76745eb..3107492a 100644 --- a/ethers-etherscan/src/utils.rs +++ b/ethers-etherscan/src/utils.rs @@ -36,20 +36,35 @@ pub fn deserialize_address_opt<'de, D: Deserializer<'de>>( /// Deserializes as JSON: /// -/// `{ "SourceCode": "{{ .. }}", ..}` +/// Object: `{ "SourceCode": { language: "Solidity", .. }, ..}` /// /// or /// -/// `{ "SourceCode": "..", .. }` -pub fn deserialize_stringified_source_code<'de, D: Deserializer<'de>>( +/// Stringified JSON: `{ "SourceCode": "{{\r\n \"language\": \"Solidity\", ..}}", ..}` +/// +/// or +/// +/// Normal source code: `{ "SourceCode": "// SPDX-License-Identifier: ...", .. }` +pub fn deserialize_source_code<'de, D: Deserializer<'de>>( deserializer: D, ) -> std::result::Result { - let s = String::deserialize(deserializer)?; - if s.starts_with("{{") && s.ends_with("}}") { - let s = &s[1..s.len() - 1]; - serde_json::from_str(s).map_err(serde::de::Error::custom) - } else { - Ok(SourceCodeMetadata::SourceCode(s)) + #[derive(Deserialize)] + #[serde(untagged)] + enum SourceCode { + String(String), // this must come first + Obj(SourceCodeMetadata), + } + let s = SourceCode::deserialize(deserializer)?; + match s { + SourceCode::String(s) => { + if s.starts_with("{{") && s.ends_with("}}") { + let s = &s[1..s.len() - 1]; + serde_json::from_str(s).map_err(serde::de::Error::custom) + } else { + Ok(SourceCodeMetadata::SourceCode(s)) + } + } + SourceCode::Obj(obj) => Ok(obj), } } @@ -108,17 +123,29 @@ mod tests { } #[test] - fn can_deserialize_stringified_source_code() { + fn can_deserialize_source_code() { #[derive(Deserialize)] struct Test { - #[serde(deserialize_with = "deserialize_stringified_source_code")] + #[serde(deserialize_with = "deserialize_source_code")] source_code: SourceCodeMetadata, } let src = "source code text"; + // Normal JSON let json = r#"{ - "source_code": "{{ \"language\": \"Solidity\", \"sources\": {\"Contract\": { \"content\": \"source code text\" } } }}" + "source_code": { "language": "Solidity", "sources": { "Contract": { "content": "source code text" } } } + }"#; + let de: Test = serde_json::from_str(json).unwrap(); + assert!(matches!(de.source_code.language().unwrap(), SourceCodeLanguage::Solidity)); + assert_eq!(de.source_code.sources().len(), 1); + assert_eq!(de.source_code.sources().get("Contract").unwrap().content, src); + #[cfg(feature = "ethers-solc")] + assert!(matches!(de.source_code.settings().unwrap(), None)); + + // Stringified JSON + let json = r#"{ + "source_code": "{{ \"language\": \"Solidity\", \"sources\": { \"Contract\": { \"content\": \"source code text\" } } }}" }"#; let de: Test = serde_json::from_str(json).unwrap(); assert!(matches!(de.source_code.language().unwrap(), SourceCodeLanguage::Solidity)); diff --git a/ethers-middleware/Cargo.toml b/ethers-middleware/Cargo.toml index 9fc5775c..08c79162 100644 --- a/ethers-middleware/Cargo.toml +++ b/ethers-middleware/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ethers-middleware" version = "1.0.2" -edition = "2018" +edition = "2021" rust-version = "1.64" authors = ["Georgios Konstantopoulos "] license = "MIT OR Apache-2.0" @@ -47,12 +47,16 @@ ethers-providers = { version = "^1.0.0", path = "../ethers-providers", default-f "ws", "rustls", ] } -once_cell = "1.16.0" +once_cell = "1.17.0" ethers-solc = { version = "^1.0.0", path = "../ethers-solc" } -serial_test = "0.9.0" +serial_test = "0.10.0" +reqwest = { version = "0.11.13", default-features = false, features = ["json", "rustls"] } [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] tokio = { version = "1.18", default-features = false, features = ["rt", "macros", "time"] } [features] +default = ["rustls"] celo = ["ethers-core/celo", "ethers-providers/celo", "ethers-signers/celo", "ethers-contract/celo"] +openssl = ["reqwest/native-tls"] +rustls = ["reqwest/rustls-tls"] diff --git a/ethers-middleware/README.md b/ethers-middleware/README.md index 97d37ab2..75a9b328 100644 --- a/ethers-middleware/README.md +++ b/ethers-middleware/README.md @@ -23,13 +23,13 @@ use ethers_providers::{Middleware, Provider, Http}; use std::sync::Arc; use std::convert::TryFrom; use ethers_signers::{LocalWallet, Signer}; -use ethers_middleware::{*,gas_oracle::*}; +use ethers_middleware::{gas_oracle::{GasOracle, GasNow}, MiddlewareBuilder}; fn builder_example() { let key = "fdb33e2105f08abe41a8ee3b758726a31abdd57b7a443f470f23efce853af169"; let signer = key.parse::().unwrap(); let address = signer.address(); - let gas_oracle = EthGasStation::new(None); + let gas_oracle = GasNow::new(); let provider = Provider::::try_from("http://localhost:8545") .unwrap() @@ -58,7 +58,7 @@ fn builder_example_wrap_into() { .unwrap() .wrap_into(|p| GasEscalatorMiddleware::new(p, escalator, Frequency::PerBlock)) .wrap_into(|p| SignerMiddleware::new(p, signer)) - .wrap_into(|p| GasOracleMiddleware::new(p, EthGasStation::new(None))) + .wrap_into(|p| GasOracleMiddleware::new(p, GasNow::new())) .wrap_into(|p| NonceManagerMiddleware::new(p, address)); // Outermost layer } ``` @@ -72,7 +72,7 @@ use ethers_providers::{Provider, Http}; use ethers_signers::{LocalWallet, Signer}; use ethers_middleware::{ gas_escalator::{GasEscalatorMiddleware, GeometricGasPrice, Frequency}, - gas_oracle::{GasOracleMiddleware, EthGasStation, GasCategory}, + gas_oracle::{GasOracleMiddleware, GasCategory, GasNow}, signer::SignerMiddleware, nonce_manager::NonceManagerMiddleware, }; @@ -91,8 +91,8 @@ let signer = LocalWallet::new(&mut rand::thread_rng()); let address = signer.address(); let provider = SignerMiddleware::new(provider, signer); -// Use EthGasStation as the gas oracle -let gas_oracle = EthGasStation::new(None); +// Use GasNow as the gas oracle +let gas_oracle = GasNow::new(); let provider = GasOracleMiddleware::new(provider, gas_oracle); // Manage nonces locally diff --git a/ethers-middleware/src/builder.rs b/ethers-middleware/src/builder.rs index b1827407..f824add9 100644 --- a/ethers-middleware/src/builder.rs +++ b/ethers-middleware/src/builder.rs @@ -16,14 +16,14 @@ use ethers_signers::Signer; /// use std::sync::Arc; /// use std::convert::TryFrom; /// use ethers_signers::{LocalWallet, Signer}; -/// use ethers_middleware::{*,gas_escalator::*,gas_oracle::*}; +/// use ethers_middleware::{*, gas_escalator::*, gas_oracle::*}; /// /// fn builder_example() { /// let key = "fdb33e2105f08abe41a8ee3b758726a31abdd57b7a443f470f23efce853af169"; /// let signer = key.parse::().unwrap(); /// let address = signer.address(); /// let escalator = GeometricGasPrice::new(1.125, 60_u64, None::); -/// let gas_oracle = EthGasStation::new(None); +/// let gas_oracle = GasNow::new(); /// /// let provider = Provider::::try_from("http://localhost:8545") /// .unwrap() @@ -43,7 +43,7 @@ use ethers_signers::Signer; /// .unwrap() /// .wrap_into(|p| GasEscalatorMiddleware::new(p, escalator, Frequency::PerBlock)) /// .wrap_into(|p| SignerMiddleware::new(p, signer)) -/// .wrap_into(|p| GasOracleMiddleware::new(p, EthGasStation::new(None))) +/// .wrap_into(|p| GasOracleMiddleware::new(p, GasNow::new())) /// .wrap_into(|p| NonceManagerMiddleware::new(p, address)); // Outermost layer /// } /// ``` diff --git a/ethers-middleware/src/gas_escalator/mod.rs b/ethers-middleware/src/gas_escalator/mod.rs index a71adbf3..f3ab1840 100644 --- a/ethers-middleware/src/gas_escalator/mod.rs +++ b/ethers-middleware/src/gas_escalator/mod.rs @@ -46,7 +46,7 @@ pub enum Frequency { /// use ethers_providers::{Provider, Http}; /// use ethers_middleware::{ /// gas_escalator::{GeometricGasPrice, Frequency, GasEscalatorMiddleware}, -/// gas_oracle::{EthGasStation, GasCategory, GasOracleMiddleware}, +/// gas_oracle::{GasNow, GasCategory, GasOracleMiddleware}, /// }; /// use std::{convert::TryFrom, time::Duration, sync::Arc}; /// @@ -60,7 +60,7 @@ pub enum Frequency { /// }; /// /// // ... proceed to wrap it in other middleware -/// let gas_oracle = EthGasStation::new(None).category(GasCategory::SafeLow); +/// let gas_oracle = GasNow::new().category(GasCategory::SafeLow); /// let provider = GasOracleMiddleware::new(provider, gas_oracle); /// ``` pub struct GasEscalatorMiddleware { diff --git a/ethers-middleware/src/gas_oracle/blocknative.rs b/ethers-middleware/src/gas_oracle/blocknative.rs index 11832c5a..84b037c0 100644 --- a/ethers-middleware/src/gas_oracle/blocknative.rs +++ b/ethers-middleware/src/gas_oracle/blocknative.rs @@ -1,13 +1,128 @@ -use crate::gas_oracle::{GasCategory, GasOracle, GasOracleError, GWEI_TO_WEI}; +use super::{from_gwei_f64, GasCategory, GasOracle, GasOracleError, Result, GWEI_TO_WEI_U256}; use async_trait::async_trait; use ethers_core::types::U256; use reqwest::{header::AUTHORIZATION, Client}; use serde::Deserialize; -use std::{collections::HashMap, convert::TryInto}; +use std::collections::HashMap; use url::Url; -const BLOCKNATIVE_GAS_PRICE_ENDPOINT: &str = "https://api.blocknative.com/gasprices/blockprices"; +const URL: &str = "https://api.blocknative.com/gasprices/blockprices"; +/// A client over HTTP for the [BlockNative](https://www.blocknative.com/gas-estimator) gas tracker API +/// that implements the `GasOracle` trait. +#[derive(Clone, Debug)] +#[must_use] +pub struct BlockNative { + client: Client, + url: Url, + api_key: Option, + gas_category: GasCategory, +} + +#[derive(Clone, Debug, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct Response { + pub system: String, + pub network: String, + pub unit: String, + pub max_price: u64, + pub block_prices: Vec, + pub estimated_base_fees: Option>>>, +} + +#[derive(Clone, Debug, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct BlockPrice { + pub block_number: u64, + pub estimated_transaction_count: u64, + pub base_fee_per_gas: f64, + pub estimated_prices: Vec, +} + +#[derive(Clone, Copy, Debug, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct GasEstimate { + pub confidence: u64, + pub price: u64, + pub max_priority_fee_per_gas: f64, + pub max_fee_per_gas: f64, +} + +#[derive(Clone, Copy, Debug, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct BaseFeeEstimate { + pub confidence: u64, + pub base_fee: f64, +} + +impl Response { + #[inline] + pub fn estimate_from_category(&self, gas_category: &GasCategory) -> Result { + let confidence = gas_category_to_confidence(gas_category); + let price = self + .block_prices + .first() + .ok_or(GasOracleError::InvalidResponse)? + .estimated_prices + .iter() + .find(|p| p.confidence == confidence) + .ok_or(GasOracleError::GasCategoryNotSupported)?; + Ok(*price) + } +} + +impl Default for BlockNative { + fn default() -> Self { + Self::new(None) + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl GasOracle for BlockNative { + async fn fetch(&self) -> Result { + let estimate = self.query().await?.estimate_from_category(&self.gas_category)?; + Ok(U256::from(estimate.price) * GWEI_TO_WEI_U256) + } + + async fn estimate_eip1559_fees(&self) -> Result<(U256, U256)> { + let estimate = self.query().await?.estimate_from_category(&self.gas_category)?; + let max = from_gwei_f64(estimate.max_fee_per_gas); + let prio = from_gwei_f64(estimate.max_priority_fee_per_gas); + Ok((max, prio)) + } +} + +impl BlockNative { + /// Creates a new [BlockNative](https://www.blocknative.com/gas-estimator) gas oracle. + pub fn new(api_key: Option) -> Self { + Self::with_client(Client::new(), api_key) + } + + /// Same as [`Self::new`] but with a custom [`Client`]. + pub fn with_client(client: Client, api_key: Option) -> Self { + let url = Url::parse(URL).unwrap(); + Self { client, api_key, url, gas_category: GasCategory::Standard } + } + + /// Sets the gas price category to be used when fetching the gas price. + pub fn category(mut self, gas_category: GasCategory) -> Self { + self.gas_category = gas_category; + self + } + + /// Perform a request to the gas price API and deserialize the response. + pub async fn query(&self) -> Result { + let mut request = self.client.get(self.url.clone()); + if let Some(api_key) = self.api_key.as_ref() { + request = request.header(AUTHORIZATION, api_key); + } + let response = request.send().await?.error_for_status()?.json().await?; + Ok(response) + } +} + +#[inline] fn gas_category_to_confidence(gas_category: &GasCategory) -> u64 { match gas_category { GasCategory::SafeLow => 80, @@ -16,124 +131,3 @@ fn gas_category_to_confidence(gas_category: &GasCategory) -> u64 { GasCategory::Fastest => 99, } } - -/// A client over HTTP for the [BlockNative](https://www.blocknative.com/gas-estimator) gas tracker API -/// that implements the `GasOracle` trait -#[derive(Clone, Debug)] -pub struct BlockNative { - client: Client, - url: Url, - api_key: String, - gas_category: GasCategory, -} - -#[derive(Debug, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct BlockNativeGasResponse { - system: Option, - network: Option, - unit: Option, - max_price: Option, - block_prices: Vec, - estimated_base_fees: Vec>>, -} - -impl BlockNativeGasResponse { - pub fn get_estimation_for( - &self, - gas_category: &GasCategory, - ) -> Result { - let confidence = gas_category_to_confidence(gas_category); - Ok(self - .block_prices - .first() - .ok_or(GasOracleError::InvalidResponse)? - .estimated_prices - .iter() - .find(|p| p.confidence == confidence) - .ok_or(GasOracleError::GasCategoryNotSupported)? - .clone()) - } -} - -#[derive(Debug, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct BlockPrice { - block_number: u64, - estimated_transaction_count: u64, - base_fee_per_gas: f64, - estimated_prices: Vec, -} - -#[derive(Clone, Debug, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct EstimatedPrice { - confidence: u64, - price: u64, - max_priority_fee_per_gas: f64, - max_fee_per_gas: f64, -} - -#[derive(Debug, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct BaseFeeEstimate { - confidence: u64, - base_fee: f64, -} - -impl BlockNative { - /// Creates a new [BlockNative](https://www.blocknative.com/gas-estimator) gas oracle. - pub fn new(api_key: String) -> Self { - Self::with_client(Client::new(), api_key) - } - - /// Same as [`Self::new`] but with a custom [`Client`]. - pub fn with_client(client: Client, api_key: String) -> Self { - Self { - client, - api_key, - url: BLOCKNATIVE_GAS_PRICE_ENDPOINT.try_into().unwrap(), - gas_category: GasCategory::Standard, - } - } - - /// Sets the gas price category to be used when fetching the gas price. - #[must_use] - pub fn category(mut self, gas_category: GasCategory) -> Self { - self.gas_category = gas_category; - self - } - - /// Perform request to Blocknative, decode response - pub async fn request(&self) -> Result { - self.client - .get(self.url.as_ref()) - .header(AUTHORIZATION, &self.api_key) - .send() - .await? - .error_for_status()? - .json() - .await - .map_err(GasOracleError::HttpClientError) - } -} - -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] -impl GasOracle for BlockNative { - async fn fetch(&self) -> Result { - let prices = self.request().await?.get_estimation_for(&self.gas_category)?; - Ok(U256::from(prices.price * 100_u64) * U256::from(GWEI_TO_WEI) / U256::from(100)) - } - - async fn estimate_eip1559_fees(&self) -> Result<(U256, U256), GasOracleError> { - let prices = self.request().await?.get_estimation_for(&self.gas_category)?; - let base_fee = U256::from((prices.max_fee_per_gas * 100.0) as u64) * - U256::from(GWEI_TO_WEI) / - U256::from(100); - let prio_fee = U256::from((prices.max_priority_fee_per_gas * 100.0) as u64) * - U256::from(GWEI_TO_WEI) / - U256::from(100); - Ok((base_fee, prio_fee)) - } -} diff --git a/ethers-middleware/src/gas_oracle/cache.rs b/ethers-middleware/src/gas_oracle/cache.rs index 6f8b162b..d840cb0f 100644 --- a/ethers-middleware/src/gas_oracle/cache.rs +++ b/ethers-middleware/src/gas_oracle/cache.rs @@ -1,4 +1,4 @@ -use crate::gas_oracle::{GasOracle, GasOracleError}; +use super::{GasOracle, Result}; use async_trait::async_trait; use ethers_core::types::U256; use futures_locks::RwLock; @@ -19,6 +19,24 @@ pub struct Cache { #[derive(Default, Debug)] struct Cached(RwLock>); +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl GasOracle for Cache { + async fn fetch(&self) -> Result { + self.fee.get(self.validity, || self.inner.fetch()).await + } + + async fn estimate_eip1559_fees(&self) -> Result<(U256, U256)> { + self.eip1559.get(self.validity, || self.inner.estimate_eip1559_fees()).await + } +} + +impl Cache { + pub fn new(validity: Duration, inner: T) -> Self { + Self { inner, validity, fee: Cached::default(), eip1559: Cached::default() } + } +} + impl Cached { async fn get(&self, validity: Duration, fetch: F) -> Result where @@ -50,21 +68,3 @@ impl Cached { } } } - -impl Cache { - pub fn new(validity: Duration, inner: T) -> Self { - Self { inner, validity, fee: Cached::default(), eip1559: Cached::default() } - } -} - -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] -impl GasOracle for Cache { - async fn fetch(&self) -> Result { - self.fee.get(self.validity, || self.inner.fetch()).await - } - - async fn estimate_eip1559_fees(&self) -> Result<(U256, U256), GasOracleError> { - self.eip1559.get(self.validity, || self.inner.estimate_eip1559_fees()).await - } -} diff --git a/ethers-middleware/src/gas_oracle/eth_gas_station.rs b/ethers-middleware/src/gas_oracle/eth_gas_station.rs index 683caf37..73f1572c 100644 --- a/ethers-middleware/src/gas_oracle/eth_gas_station.rs +++ b/ethers-middleware/src/gas_oracle/eth_gas_station.rs @@ -1,88 +1,78 @@ -use std::collections::HashMap; - -use ethers_core::types::U256; +#![allow(deprecated)] +use super::{GasCategory, GasOracle, GasOracleError, Result, GWEI_TO_WEI_U256}; use async_trait::async_trait; +use ethers_core::types::U256; use reqwest::Client; use serde::Deserialize; +use std::collections::HashMap; use url::Url; -use crate::gas_oracle::{GasCategory, GasOracle, GasOracleError, GWEI_TO_WEI}; +const URL: &str = "https://ethgasstation.info/api/ethgasAPI.json"; -const ETH_GAS_STATION_URL_PREFIX: &str = "https://ethgasstation.info/api/ethgasAPI.json"; - -/// A client over HTTP for the [EthGasStation](https://ethgasstation.info/api/ethgasAPI.json) gas tracker API -/// that implements the `GasOracle` trait +/// A client over HTTP for the [EthGasStation](https://ethgasstation.info) gas tracker API +/// that implements the `GasOracle` trait. #[derive(Clone, Debug)] +#[deprecated = "ETHGasStation is shutting down: https://twitter.com/ETHGasStation/status/1597341610777317376"] +#[must_use] pub struct EthGasStation { client: Client, url: Url, gas_category: GasCategory, } -#[derive(Clone, Debug, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] /// Eth Gas Station's response for the current recommended fast, standard and /// safe low gas prices on the Ethereum network, along with the current block /// and wait times for each "speed". -pub struct EthGasStationResponse { - /// Recommended safe(expected to be mined in < 30 minutes) gas price in - /// x10 Gwei (divide by 10 to convert it to gwei) - pub safe_low: f64, - /// Recommended average(expected to be mined in < 5 minutes) gas price in - /// x10 Gwei (divide by 10 to convert it to gwei) +#[derive(Clone, Debug, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct Response { + /// Recommended safe (expected to be mined in < 30 minutes). + /// + /// In gwei * 10 (divide by 10 to convert it to gwei). + pub safe_low: u64, + /// Recommended average (expected to be mined in < 5 minutes). + /// + /// In gwei * 10 (divide by 10 to convert it to gwei). pub average: u64, - /// Recommended fast(expected to be mined in < 2 minutes) gas price in - /// x10 Gwei (divide by 10 to convert it to gwei) + /// Recommended fast (expected to be mined in < 2 minutes). + /// + /// In gwei * 10 (divide by 10 to convert it to gwei). pub fast: u64, - /// Recommended fastest(expected to be mined in < 30 seconds) gas price - /// in x10 Gwei(divide by 10 to convert it to gwei) + /// Recommended fastest (expected to be mined in < 30 seconds). + /// + /// In gwei * 10 (divide by 10 to convert it to gwei). pub fastest: u64, // post eip-1559 fields + /// Average time (in seconds) to mine a single block. #[serde(rename = "block_time")] // inconsistent json response naming... - /// Average time(in seconds) to mine one single block pub block_time: f64, - /// The latest block number + /// The latest block number. pub block_num: u64, - /// Smallest value of (gasUsed / gaslimit) from last 10 blocks + /// Smallest value of `gasUsed / gaslimit` from the last 10 blocks. pub speed: f64, - /// Waiting time(in minutes) for the `safe_low` gas price + /// Waiting time (in minutes) for the `safe_low` gas price. pub safe_low_wait: f64, - /// Waiting time(in minutes) for the `average` gas price + /// Waiting time (in minutes) for the `average` gas price. pub avg_wait: f64, - /// Waiting time(in minutes) for the `fast` gas price + /// Waiting time (in minutes) for the `fast` gas price. pub fast_wait: f64, - /// Waiting time(in minutes) for the `fastest` gas price + /// Waiting time (in minutes) for the `fastest` gas price. pub fastest_wait: f64, // What is this? pub gas_price_range: HashMap, } -impl EthGasStation { - /// Creates a new [EthGasStation](https://docs.ethgasstation.info/) gas oracle - pub fn new(api_key: Option<&str>) -> Self { - Self::with_client(Client::new(), api_key) - } - - /// Same as [`Self::new`] but with a custom [`Client`]. - pub fn with_client(client: Client, api_key: Option<&str>) -> Self { - let mut url = Url::parse(ETH_GAS_STATION_URL_PREFIX).expect("invalid url"); - if let Some(key) = api_key { - url.query_pairs_mut().append_pair("api-key", key); +impl Response { + #[inline] + pub fn gas_from_category(&self, gas_category: GasCategory) -> u64 { + match gas_category { + GasCategory::SafeLow => self.safe_low, + GasCategory::Standard => self.average, + GasCategory::Fast => self.fast, + GasCategory::Fastest => self.fastest, } - EthGasStation { client, url, gas_category: GasCategory::Standard } - } - - /// Sets the gas price category to be used when fetching the gas price. - #[must_use] - pub fn category(mut self, gas_category: GasCategory) -> Self { - self.gas_category = gas_category; - self - } - - pub async fn query(&self) -> Result { - Ok(self.client.get(self.url.as_ref()).send().await?.json::().await?) } } @@ -95,19 +85,43 @@ impl Default for EthGasStation { #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl GasOracle for EthGasStation { - async fn fetch(&self) -> Result { + async fn fetch(&self) -> Result { let res = self.query().await?; - let gas_price = match self.gas_category { - GasCategory::SafeLow => U256::from((res.safe_low.ceil() as u64 * GWEI_TO_WEI) / 10), - GasCategory::Standard => U256::from((res.average * GWEI_TO_WEI) / 10), - GasCategory::Fast => U256::from((res.fast * GWEI_TO_WEI) / 10), - GasCategory::Fastest => U256::from((res.fastest * GWEI_TO_WEI) / 10), - }; - - Ok(gas_price) + let gas_price = res.gas_from_category(self.gas_category); + // gas_price is in `gwei * 10` + Ok(U256::from(gas_price) * GWEI_TO_WEI_U256 / U256::from(10_u64)) } - async fn estimate_eip1559_fees(&self) -> Result<(U256, U256), GasOracleError> { + async fn estimate_eip1559_fees(&self) -> Result<(U256, U256)> { Err(GasOracleError::Eip1559EstimationNotSupported) } } + +impl EthGasStation { + /// Creates a new [EthGasStation](https://docs.ethgasstation.info/) gas oracle. + pub fn new(api_key: Option<&str>) -> Self { + Self::with_client(Client::new(), api_key) + } + + /// Same as [`Self::new`] but with a custom [`Client`]. + pub fn with_client(client: Client, api_key: Option<&str>) -> Self { + let mut url = Url::parse(URL).unwrap(); + if let Some(key) = api_key { + url.query_pairs_mut().append_pair("api-key", key); + } + EthGasStation { client, url, gas_category: GasCategory::Standard } + } + + /// Sets the gas price category to be used when fetching the gas price. + pub fn category(mut self, gas_category: GasCategory) -> Self { + self.gas_category = gas_category; + self + } + + /// Perform a request to the gas price API and deserialize the response. + pub async fn query(&self) -> Result { + let response = + self.client.get(self.url.clone()).send().await?.error_for_status()?.json().await?; + Ok(response) + } +} diff --git a/ethers-middleware/src/gas_oracle/etherchain.rs b/ethers-middleware/src/gas_oracle/etherchain.rs index 3fc94e6a..5d419929 100644 --- a/ethers-middleware/src/gas_oracle/etherchain.rs +++ b/ethers-middleware/src/gas_oracle/etherchain.rs @@ -1,56 +1,42 @@ -use ethers_core::types::U256; - +use super::{from_gwei_f64, GasCategory, GasOracle, GasOracleError, Result}; use async_trait::async_trait; +use ethers_core::types::U256; use reqwest::Client; use serde::Deserialize; use url::Url; -use crate::gas_oracle::{GasCategory, GasOracle, GasOracleError, GWEI_TO_WEI}; - -const ETHERCHAIN_URL: &str = "https://www.etherchain.org/api/gasPriceOracle"; +const URL: &str = "https://www.etherchain.org/api/gasPriceOracle"; /// A client over HTTP for the [Etherchain](https://www.etherchain.org/api/gasPriceOracle) gas tracker API -/// that implements the `GasOracle` trait +/// that implements the `GasOracle` trait. #[derive(Clone, Debug)] +#[must_use] pub struct Etherchain { client: Client, url: Url, gas_category: GasCategory, } -#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Debug, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] -pub struct EtherchainResponse { - pub safe_low: f32, - pub standard: f32, - pub fast: f32, - pub fastest: f32, - pub current_base_fee: f32, - pub recommended_base_fee: f32, +pub struct Response { + pub safe_low: f64, + pub standard: f64, + pub fast: f64, + pub fastest: f64, + pub current_base_fee: f64, + pub recommended_base_fee: f64, } -impl Etherchain { - /// Creates a new [Etherchain](https://etherchain.org/tools/gasPriceOracle) gas price oracle. - pub fn new() -> Self { - Self::with_client(Client::new()) - } - - /// Same as [`Self::new`] but with a custom [`Client`]. - pub fn with_client(client: Client) -> Self { - let url = Url::parse(ETHERCHAIN_URL).expect("invalid url"); - - Etherchain { client, url, gas_category: GasCategory::Standard } - } - - /// Sets the gas price category to be used when fetching the gas price. - #[must_use] - pub fn category(mut self, gas_category: GasCategory) -> Self { - self.gas_category = gas_category; - self - } - - pub async fn query(&self) -> Result { - Ok(self.client.get(self.url.as_ref()).send().await?.json::().await?) +impl Response { + #[inline] + pub fn gas_from_category(&self, gas_category: GasCategory) -> f64 { + match gas_category { + GasCategory::SafeLow => self.safe_low, + GasCategory::Standard => self.standard, + GasCategory::Fast => self.fast, + GasCategory::Fastest => self.fastest, + } } } @@ -63,19 +49,39 @@ impl Default for Etherchain { #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl GasOracle for Etherchain { - async fn fetch(&self) -> Result { + async fn fetch(&self) -> Result { let res = self.query().await?; - let gas_price = match self.gas_category { - GasCategory::SafeLow => U256::from((res.safe_low as u64) * GWEI_TO_WEI), - GasCategory::Standard => U256::from((res.standard as u64) * GWEI_TO_WEI), - GasCategory::Fast => U256::from((res.fast as u64) * GWEI_TO_WEI), - GasCategory::Fastest => U256::from((res.fastest as u64) * GWEI_TO_WEI), - }; - - Ok(gas_price) + let gas_price = res.gas_from_category(self.gas_category); + Ok(from_gwei_f64(gas_price)) } - async fn estimate_eip1559_fees(&self) -> Result<(U256, U256), GasOracleError> { + async fn estimate_eip1559_fees(&self) -> Result<(U256, U256)> { Err(GasOracleError::Eip1559EstimationNotSupported) } } + +impl Etherchain { + /// Creates a new [Etherchain](https://etherchain.org/tools/gasPriceOracle) gas price oracle. + pub fn new() -> Self { + Self::with_client(Client::new()) + } + + /// Same as [`Self::new`] but with a custom [`Client`]. + pub fn with_client(client: Client) -> Self { + let url = Url::parse(URL).unwrap(); + Etherchain { client, url, gas_category: GasCategory::Standard } + } + + /// Sets the gas price category to be used when fetching the gas price. + pub fn category(mut self, gas_category: GasCategory) -> Self { + self.gas_category = gas_category; + self + } + + /// Perform a request to the gas price API and deserialize the response. + pub async fn query(&self) -> Result { + let response = + self.client.get(self.url.clone()).send().await?.error_for_status()?.json().await?; + Ok(response) + } +} diff --git a/ethers-middleware/src/gas_oracle/etherscan.rs b/ethers-middleware/src/gas_oracle/etherscan.rs index b39cebff..9d8da4c0 100644 --- a/ethers-middleware/src/gas_oracle/etherscan.rs +++ b/ethers-middleware/src/gas_oracle/etherscan.rs @@ -1,18 +1,57 @@ +use super::{GasCategory, GasOracle, GasOracleError, Result, GWEI_TO_WEI_U256}; use async_trait::async_trait; - use ethers_core::types::U256; use ethers_etherscan::Client; - -use crate::gas_oracle::{GasCategory, GasOracle, GasOracleError, GWEI_TO_WEI}; +use std::ops::{Deref, DerefMut}; /// A client over HTTP for the [Etherscan](https://api.etherscan.io/api?module=gastracker&action=gasoracle) gas tracker API /// that implements the `GasOracle` trait #[derive(Clone, Debug)] +#[must_use] pub struct Etherscan { client: Client, gas_category: GasCategory, } +impl Deref for Etherscan { + type Target = Client; + + fn deref(&self) -> &Self::Target { + &self.client + } +} + +impl DerefMut for Etherscan { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.client + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl GasOracle for Etherscan { + async fn fetch(&self) -> Result { + // handle unsupported gas categories before making the request + match self.gas_category { + GasCategory::SafeLow | GasCategory::Standard | GasCategory::Fast => {} + GasCategory::Fastest => return Err(GasOracleError::GasCategoryNotSupported), + } + + let result = self.query().await?; + let gas_price = match self.gas_category { + GasCategory::SafeLow => result.safe_gas_price, + GasCategory::Standard => result.propose_gas_price, + GasCategory::Fast => result.fast_gas_price, + _ => unreachable!(), + }; + Ok(U256::from(gas_price) * GWEI_TO_WEI_U256) + } + + async fn estimate_eip1559_fees(&self) -> Result<(U256, U256)> { + Err(GasOracleError::Eip1559EstimationNotSupported) + } +} + impl Etherscan { /// Creates a new [Etherscan](https://etherscan.io/gastracker) gas price oracle. pub fn new(client: Client) -> Self { @@ -20,32 +59,13 @@ impl Etherscan { } /// Sets the gas price category to be used when fetching the gas price. - #[must_use] pub fn category(mut self, gas_category: GasCategory) -> Self { self.gas_category = gas_category; self } -} -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] -impl GasOracle for Etherscan { - async fn fetch(&self) -> Result { - if matches!(self.gas_category, GasCategory::Fastest) { - return Err(GasOracleError::GasCategoryNotSupported) - } - - let result = self.client.gas_oracle().await?; - - match self.gas_category { - GasCategory::SafeLow => Ok(U256::from(result.safe_gas_price * GWEI_TO_WEI)), - GasCategory::Standard => Ok(U256::from(result.propose_gas_price * GWEI_TO_WEI)), - GasCategory::Fast => Ok(U256::from(result.fast_gas_price * GWEI_TO_WEI)), - _ => Err(GasOracleError::GasCategoryNotSupported), - } - } - - async fn estimate_eip1559_fees(&self) -> Result<(U256, U256), GasOracleError> { - Err(GasOracleError::Eip1559EstimationNotSupported) + /// Perform a request to the gas price API and deserialize the response. + pub async fn query(&self) -> Result { + Ok(self.client.gas_oracle().await?) } } diff --git a/ethers-middleware/src/gas_oracle/gas_now.rs b/ethers-middleware/src/gas_oracle/gas_now.rs index 65ddc46c..29de2c46 100644 --- a/ethers-middleware/src/gas_oracle/gas_now.rs +++ b/ethers-middleware/src/gas_oracle/gas_now.rs @@ -1,64 +1,54 @@ -use ethers_core::types::U256; - +use super::{GasCategory, GasOracle, GasOracleError, Result}; use async_trait::async_trait; +use ethers_core::types::U256; use reqwest::Client; use serde::Deserialize; use url::Url; -use crate::gas_oracle::{GasCategory, GasOracle, GasOracleError}; +const URL: &str = "https://beaconcha.in/api/v1/execution/gasnow"; -const GAS_NOW_URL: &str = "https://etherchain.org/api/gasnow"; - -/// A client over HTTP for the [Etherchain GasNow](https://etherchain.org/tools/gasnow) gas tracker API -/// that implements the `GasOracle` trait +/// A client over HTTP for the [beaconcha.in GasNow](https://beaconcha.in/gasnow) gas tracker API +/// that implements the `GasOracle` trait. #[derive(Clone, Debug)] +#[must_use] pub struct GasNow { client: Client, url: Url, gas_category: GasCategory, } -#[derive(Deserialize)] -struct GasNowResponseWrapper { - data: GasNowResponse, +#[derive(Clone, Copy, Debug, Deserialize, PartialEq)] +pub struct Response { + pub code: u64, + pub data: ResponseData, } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord)] -pub struct GasNowResponse { +#[derive(Clone, Copy, Debug, Deserialize, PartialEq)] +pub struct ResponseData { pub rapid: u64, pub fast: u64, pub standard: u64, pub slow: u64, + pub timestamp: u64, + #[serde(rename = "priceUSD")] + pub price_usd: f64, } -impl GasNow { - /// Creates a new [Etherchain GasNow](https://etherchain.org/tools/gasnow) gas price oracle. - pub fn new() -> Self { - Self::with_client(Client::new()) +impl Response { + #[inline] + pub fn gas_from_category(&self, gas_category: GasCategory) -> u64 { + self.data.gas_from_category(gas_category) } +} - /// Same as [`Self::new`] but with a custom [`Client`]. - pub fn with_client(client: Client) -> Self { - let url = Url::parse(GAS_NOW_URL).expect("invalid url"); - - Self { client, url, gas_category: GasCategory::Standard } - } - - /// Sets the gas price category to be used when fetching the gas price. - pub fn category(mut self, gas_category: GasCategory) -> Self { - self.gas_category = gas_category; - self - } - - pub async fn query(&self) -> Result { - let res = self - .client - .get(self.url.as_ref()) - .send() - .await? - .json::() - .await?; - Ok(res.data) +impl ResponseData { + fn gas_from_category(&self, gas_category: GasCategory) -> u64 { + match gas_category { + GasCategory::SafeLow => self.slow, + GasCategory::Standard => self.standard, + GasCategory::Fast => self.fast, + GasCategory::Fastest => self.rapid, + } } } @@ -71,19 +61,39 @@ impl Default for GasNow { #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl GasOracle for GasNow { - async fn fetch(&self) -> Result { + async fn fetch(&self) -> Result { let res = self.query().await?; - let gas_price = match self.gas_category { - GasCategory::SafeLow => U256::from(res.slow), - GasCategory::Standard => U256::from(res.standard), - GasCategory::Fast => U256::from(res.fast), - GasCategory::Fastest => U256::from(res.rapid), - }; - - Ok(gas_price) + let gas_price = res.gas_from_category(self.gas_category); + Ok(U256::from(gas_price)) } - async fn estimate_eip1559_fees(&self) -> Result<(U256, U256), GasOracleError> { + async fn estimate_eip1559_fees(&self) -> Result<(U256, U256)> { Err(GasOracleError::Eip1559EstimationNotSupported) } } + +impl GasNow { + /// Creates a new [beaconcha.in GasNow](https://beaconcha.in/gasnow) gas price oracle. + pub fn new() -> Self { + Self::with_client(Client::new()) + } + + /// Same as [`Self::new`] but with a custom [`Client`]. + pub fn with_client(client: Client) -> Self { + let url = Url::parse(URL).unwrap(); + Self { client, url, gas_category: GasCategory::Standard } + } + + /// Sets the gas price category to be used when fetching the gas price. + pub fn category(mut self, gas_category: GasCategory) -> Self { + self.gas_category = gas_category; + self + } + + /// Perform a request to the gas price API and deserialize the response. + pub async fn query(&self) -> Result { + let response = + self.client.get(self.url.as_ref()).send().await?.error_for_status()?.json().await?; + Ok(response) + } +} diff --git a/ethers-middleware/src/gas_oracle/median.rs b/ethers-middleware/src/gas_oracle/median.rs index 2149ce41..c6dd6c7e 100644 --- a/ethers-middleware/src/gas_oracle/median.rs +++ b/ethers-middleware/src/gas_oracle/median.rs @@ -1,4 +1,4 @@ -use crate::gas_oracle::{GasOracle, GasOracleError}; +use super::{GasOracle, GasOracleError, Result}; use async_trait::async_trait; use ethers_core::types::U256; use futures_util::future::join_all; @@ -28,13 +28,10 @@ impl Median { self.oracles.push((weight, Box::new(oracle))); } - pub async fn query_all<'a, Fn, Fut, O>( - &'a self, - mut f: Fn, - ) -> Result, GasOracleError> + pub async fn query_all<'a, Fn, Fut, O>(&'a self, mut f: Fn) -> Result> where Fn: FnMut(&'a dyn GasOracle) -> Fut, - Fut: Future>, + Fut: Future>, { // Process the oracles in parallel let futures = self.oracles.iter().map(|(_, oracle)| f(oracle.as_ref())); @@ -62,13 +59,13 @@ impl Median { #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl GasOracle for Median { - async fn fetch(&self) -> Result { + async fn fetch(&self) -> Result { let mut values = self.query_all(|oracle| oracle.fetch()).await?; // `query_all` guarantees `values` is not empty Ok(*weighted_fractile_by_key(0.5, &mut values, |fee| fee).unwrap()) } - async fn estimate_eip1559_fees(&self) -> Result<(U256, U256), GasOracleError> { + async fn estimate_eip1559_fees(&self) -> Result<(U256, U256)> { let mut values = self.query_all(|oracle| oracle.estimate_eip1559_fees()).await?; // `query_all` guarantees `values` is not empty Ok(( diff --git a/ethers-middleware/src/gas_oracle/middleware.rs b/ethers-middleware/src/gas_oracle/middleware.rs index 272b0150..918ec4b0 100644 --- a/ethers-middleware/src/gas_oracle/middleware.rs +++ b/ethers-middleware/src/gas_oracle/middleware.rs @@ -4,8 +4,8 @@ use ethers_core::types::{transaction::eip2718::TypedTransaction, *}; use ethers_providers::{FromErr, Middleware, PendingTransaction}; use thiserror::Error; +/// Middleware used for fetching gas prices over an API instead of `eth_gasPrice`. #[derive(Debug)] -/// Middleware used for fetching gas prices over an API instead of `eth_gasPrice` pub struct GasOracleMiddleware { inner: M, gas_oracle: G, @@ -21,7 +21,7 @@ where } } -#[derive(Error, Debug)] +#[derive(Debug, Error)] pub enum MiddlewareError { #[error(transparent)] GasOracleError(#[from] GasOracleError), diff --git a/ethers-middleware/src/gas_oracle/mod.rs b/ethers-middleware/src/gas_oracle/mod.rs index 4a7a701c..40c1bf42 100644 --- a/ethers-middleware/src/gas_oracle/mod.rs +++ b/ethers-middleware/src/gas_oracle/mod.rs @@ -1,54 +1,58 @@ -mod blocknative; +pub mod blocknative; pub use blocknative::BlockNative; -mod eth_gas_station; +pub mod eth_gas_station; +#[allow(deprecated)] pub use eth_gas_station::EthGasStation; -mod etherchain; +pub mod etherchain; pub use etherchain::Etherchain; -mod etherscan; +pub mod etherscan; pub use etherscan::Etherscan; -mod middleware; +pub mod middleware; pub use middleware::{GasOracleMiddleware, MiddlewareError}; -mod median; +pub mod median; pub use median::Median; -mod cache; +pub mod cache; pub use cache::Cache; -mod polygon; +pub mod polygon; pub use polygon::Polygon; -mod gas_now; +pub mod gas_now; pub use gas_now::GasNow; -mod provider_oracle; +pub mod provider_oracle; pub use provider_oracle::ProviderOracle; -use ethers_core::types::U256; - use async_trait::async_trait; use auto_impl::auto_impl; +use ethers_core::types::U256; use reqwest::Error as ReqwestError; -use std::error::Error; +use std::{error::Error, fmt::Debug}; use thiserror::Error; -const GWEI_TO_WEI: u64 = 1000000000; +pub(crate) const GWEI_TO_WEI: u64 = 1_000_000_000; +pub(crate) const GWEI_TO_WEI_U256: U256 = U256([GWEI_TO_WEI, 0, 0, 0]); -/// Various gas price categories. Choose one of the available -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub type Result = std::result::Result; + +/// Generic [`GasOracle`] gas price categories. +#[derive(Clone, Copy, Default, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum GasCategory { SafeLow, + #[default] Standard, Fast, Fastest, } -#[derive(Error, Debug)] -/// Error thrown when fetching data from the `GasOracle` +/// Error thrown by a [`GasOracle`]. +#[derive(Debug, Error)] pub enum GasOracleError { /// An internal error in the HTTP request made from the underlying /// gas oracle @@ -83,48 +87,81 @@ pub enum GasOracleError { UnsupportedChain, /// Error thrown when the provider failed. - #[error("Chain is not supported by the oracle")] + #[error("Provider error: {0}")] ProviderError(#[from] Box), } -/// `GasOracle` is a trait that an underlying gas oracle needs to implement. +/// An Ethereum gas price oracle. /// /// # Example /// /// ```no_run -/// use ethers_middleware::{ -/// gas_oracle::{EthGasStation, Etherscan, GasCategory, GasOracle}, -/// }; +/// use ethers_core::types::U256; +/// use ethers_middleware::gas_oracle::{GasCategory, GasNow, GasOracle}; /// /// # async fn foo() -> Result<(), Box> { -/// let eth_gas_station_oracle = EthGasStation::new(Some("my-api-key")); -/// let etherscan_oracle = EthGasStation::new(None).category(GasCategory::SafeLow); -/// -/// let data_1 = eth_gas_station_oracle.fetch().await?; -/// let data_2 = etherscan_oracle.fetch().await?; +/// let oracle = GasNow::default().category(GasCategory::SafeLow); +/// let gas_price = oracle.fetch().await?; +/// assert!(gas_price > U256::zero()); /// # Ok(()) /// # } /// ``` #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[auto_impl(&, Box, Arc)] -pub trait GasOracle: Send + Sync + std::fmt::Debug { - /// Makes an asynchronous HTTP query to the underlying `GasOracle` +pub trait GasOracle: Send + Sync + Debug { + /// Makes an asynchronous HTTP query to the underlying [`GasOracle`] to fetch the current gas + /// price estimate. /// /// # Example /// - /// ``` - /// use ethers_middleware::{ - /// gas_oracle::{Etherchain, GasCategory, GasOracle}, - /// }; + /// ```no_run + /// use ethers_core::types::U256; + /// use ethers_middleware::gas_oracle::{GasCategory, GasNow, GasOracle}; /// /// # async fn foo() -> Result<(), Box> { - /// let etherchain_oracle = Etherchain::new().category(GasCategory::Fastest); - /// let data = etherchain_oracle.fetch().await?; + /// let oracle = GasNow::default().category(GasCategory::SafeLow); + /// let gas_price = oracle.fetch().await?; + /// assert!(gas_price > U256::zero()); /// # Ok(()) /// # } /// ``` - async fn fetch(&self) -> Result; + async fn fetch(&self) -> Result; - async fn estimate_eip1559_fees(&self) -> Result<(U256, U256), GasOracleError>; + /// Makes an asynchronous HTTP query to the underlying [`GasOracle`] to fetch the current max + /// gas fee and priority gas fee estimates. + /// + /// # Example + /// + /// ```no_run + /// use ethers_core::types::U256; + /// use ethers_middleware::gas_oracle::{GasCategory, GasNow, GasOracle}; + /// + /// # async fn foo() -> Result<(), Box> { + /// let oracle = GasNow::default().category(GasCategory::SafeLow); + /// let (max_fee, priority_fee) = oracle.estimate_eip1559_fees().await?; + /// assert!(max_fee > U256::zero()); + /// assert!(priority_fee > U256::zero()); + /// # Ok(()) + /// # } + /// ``` + async fn estimate_eip1559_fees(&self) -> Result<(U256, U256)>; +} + +#[inline] +#[doc(hidden)] +pub(crate) fn from_gwei_f64(gwei: f64) -> U256 { + ethers_core::types::u256_from_f64_saturating(gwei) * GWEI_TO_WEI_U256 +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_gwei_wei_constants() { + let as_u256: U256 = GWEI_TO_WEI.into(); + assert_eq!(as_u256, GWEI_TO_WEI_U256); + assert_eq!(GWEI_TO_WEI_U256.as_u64(), GWEI_TO_WEI); + } } diff --git a/ethers-middleware/src/gas_oracle/polygon.rs b/ethers-middleware/src/gas_oracle/polygon.rs index 47a3415d..f7acf85a 100644 --- a/ethers-middleware/src/gas_oracle/polygon.rs +++ b/ethers-middleware/src/gas_oracle/polygon.rs @@ -1,16 +1,17 @@ -use crate::gas_oracle::{GasCategory, GasOracle, GasOracleError}; +use super::{from_gwei_f64, GasCategory, GasOracle, GasOracleError, Result}; use async_trait::async_trait; -use ethers_core::types::{u256_from_f64_saturating, Chain, U256}; +use ethers_core::types::{Chain, U256}; use reqwest::Client; use serde::Deserialize; use url::Url; -const GAS_PRICE_ENDPOINT: &str = "https://gasstation-mainnet.matic.network/v2"; -const MUMBAI_GAS_PRICE_ENDPOINT: &str = "https://gasstation-mumbai.matic.today/v2"; +const MAINNET_URL: &str = "https://gasstation-mainnet.matic.network/v2"; +const MUMBAI_URL: &str = "https://gasstation-mumbai.matic.today/v2"; /// The [Polygon](https://docs.polygon.technology/docs/develop/tools/polygon-gas-station/) gas station API -/// Queries over HTTP and implements the `GasOracle` trait +/// Queries over HTTP and implements the `GasOracle` trait. #[derive(Clone, Debug)] +#[must_use] pub struct Polygon { client: Client, url: Url, @@ -18,74 +19,98 @@ pub struct Polygon { } /// The response from the Polygon gas station API. -/// Gas prices are in Gwei. +/// +/// Gas prices are in __Gwei__. #[derive(Debug, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Response { - estimated_base_fee: f64, - safe_low: GasEstimate, - standard: GasEstimate, - fast: GasEstimate, + pub estimated_base_fee: f64, + pub safe_low: GasEstimate, + pub standard: GasEstimate, + pub fast: GasEstimate, } #[derive(Clone, Copy, Debug, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct GasEstimate { - max_priority_fee: f64, - max_fee: f64, + pub max_priority_fee: f64, + pub max_fee: f64, } -impl Polygon { - pub fn new(chain: Chain) -> Result { - Self::with_client(Client::new(), chain) +impl Response { + #[inline] + pub fn estimate_from_category(&self, gas_category: GasCategory) -> GasEstimate { + match gas_category { + GasCategory::SafeLow => self.safe_low, + GasCategory::Standard => self.standard, + GasCategory::Fast => self.fast, + GasCategory::Fastest => self.fast, + } } +} - pub fn with_client(client: Client, chain: Chain) -> Result { - // TODO: Sniff chain from chain id. - let url = match chain { - Chain::Polygon => Url::parse(GAS_PRICE_ENDPOINT).unwrap(), - Chain::PolygonMumbai => Url::parse(MUMBAI_GAS_PRICE_ENDPOINT).unwrap(), - _ => return Err(GasOracleError::UnsupportedChain), - }; - Ok(Self { client, url, gas_category: GasCategory::Standard }) - } - - /// Sets the gas price category to be used when fetching the gas price. - #[must_use] - pub fn category(mut self, gas_category: GasCategory) -> Self { - self.gas_category = gas_category; - self - } - - /// Perform request to Blocknative, decode response - pub async fn request(&self) -> Result<(f64, GasEstimate), GasOracleError> { - let response: Response = - self.client.get(self.url.as_ref()).send().await?.error_for_status()?.json().await?; - let estimate = match self.gas_category { - GasCategory::SafeLow => response.safe_low, - GasCategory::Standard => response.standard, - GasCategory::Fast => response.fast, - GasCategory::Fastest => response.fast, - }; - Ok((response.estimated_base_fee, estimate)) +impl Default for Polygon { + fn default() -> Self { + Self::new(Chain::Polygon).unwrap() } } #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl GasOracle for Polygon { - async fn fetch(&self) -> Result { - let (base_fee, estimate) = self.request().await?; - let fee = base_fee + estimate.max_priority_fee; - Ok(from_gwei(fee)) + async fn fetch(&self) -> Result { + let response = self.query().await?; + let base = response.estimated_base_fee; + let prio = response.estimate_from_category(self.gas_category).max_priority_fee; + let fee = base + prio; + Ok(from_gwei_f64(fee)) } - async fn estimate_eip1559_fees(&self) -> Result<(U256, U256), GasOracleError> { - let (_, estimate) = self.request().await?; - Ok((from_gwei(estimate.max_fee), from_gwei(estimate.max_priority_fee))) + async fn estimate_eip1559_fees(&self) -> Result<(U256, U256)> { + let response = self.query().await?; + let estimate = response.estimate_from_category(self.gas_category); + let max = from_gwei_f64(estimate.max_fee); + let prio = from_gwei_f64(estimate.max_priority_fee); + Ok((max, prio)) } } -fn from_gwei(gwei: f64) -> U256 { - u256_from_f64_saturating(gwei * 1.0e9_f64) +impl Polygon { + pub fn new(chain: Chain) -> Result { + Self::with_client(Client::new(), chain) + } + + pub fn with_client(client: Client, chain: Chain) -> Result { + // TODO: Sniff chain from chain id. + let url = match chain { + Chain::Polygon => MAINNET_URL, + Chain::PolygonMumbai => MUMBAI_URL, + _ => return Err(GasOracleError::UnsupportedChain), + }; + Ok(Self { client, url: Url::parse(url).unwrap(), gas_category: GasCategory::Standard }) + } + + /// Sets the gas price category to be used when fetching the gas price. + pub fn category(mut self, gas_category: GasCategory) -> Self { + self.gas_category = gas_category; + self + } + + /// Perform a request to the gas price API and deserialize the response. + pub async fn query(&self) -> Result { + let response = + self.client.get(self.url.clone()).send().await?.error_for_status()?.json().await?; + Ok(response) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_polygon_gas_station_response() { + let s = r#"{"safeLow":{"maxPriorityFee":2.1267086610666666,"maxFee":2.1267086760666665},"standard":{"maxPriorityFee":2.3482958369333335,"maxFee":2.3482958519333335},"fast":{"maxPriorityFee":2.793454819,"maxFee":2.793454834},"estimatedBaseFee":1.5e-8,"blockTime":2,"blockNumber":30328888}"#; + let _resp: Response = serde_json::from_str(s).unwrap(); + } } diff --git a/ethers-middleware/src/gas_oracle/provider_oracle.rs b/ethers-middleware/src/gas_oracle/provider_oracle.rs index 894bb5ab..c97c857d 100644 --- a/ethers-middleware/src/gas_oracle/provider_oracle.rs +++ b/ethers-middleware/src/gas_oracle/provider_oracle.rs @@ -1,4 +1,4 @@ -use crate::gas_oracle::{GasOracle, GasOracleError}; +use super::{GasOracle, GasOracleError, Result}; use async_trait::async_trait; use ethers_core::types::U256; use ethers_providers::Middleware; @@ -6,7 +6,8 @@ use std::fmt::Debug; /// Gas oracle from a [`Middleware`] implementation such as an /// Ethereum RPC provider. -#[derive(Debug)] +#[derive(Clone, Debug)] +#[must_use] pub struct ProviderOracle { provider: M, } @@ -21,16 +22,16 @@ impl ProviderOracle { #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl GasOracle for ProviderOracle where - ::Error: 'static, + M::Error: 'static, { - async fn fetch(&self) -> Result { + async fn fetch(&self) -> Result { self.provider .get_gas_price() .await .map_err(|err| GasOracleError::ProviderError(Box::new(err))) } - async fn estimate_eip1559_fees(&self) -> Result<(U256, U256), GasOracleError> { + async fn estimate_eip1559_fees(&self) -> Result<(U256, U256)> { // TODO: Allow configuring different estimation functions. self.provider .estimate_eip1559_fees(None) diff --git a/ethers-middleware/src/transformer/ds_proxy/factory.rs b/ethers-middleware/src/transformer/ds_proxy/factory.rs index 11fb1380..122c7bf1 100644 --- a/ethers-middleware/src/transformer/ds_proxy/factory.rs +++ b/ethers-middleware/src/transformer/ds_proxy/factory.rs @@ -1,22 +1,21 @@ use ethers_contract::Lazy; use ethers_core::types::*; -use std::{collections::HashMap, str::FromStr}; +use std::collections::HashMap; /// A lazily computed hash map with the Ethereum network IDs as keys and the corresponding /// DsProxyFactory contract addresses as values pub static ADDRESS_BOOK: Lazy> = Lazy::new(|| { - let mut m = HashMap::new(); + let mut m = HashMap::with_capacity(1); // mainnet - let addr = - Address::from_str("eefba1e63905ef1d7acba5a8513c70307c1ce441").expect("Decoding failed"); - m.insert(U256::from(1u8), addr); + let addr = "eefba1e63905ef1d7acba5a8513c70307c1ce441".parse().unwrap(); + m.insert(U256::from(1_u64), addr); m }); +/// Generated with abigen: /// -/// Generated with /// ```ignore /// # use ethers_contract::abigen; /// abigen!(DsProxyFactory, @@ -26,7 +25,6 @@ pub static ADDRESS_BOOK: Lazy> = Lazy::new(|| { /// } /// ); /// ``` -// Auto-generated type-safe bindings pub use dsproxyfactory_mod::*; #[allow(clippy::too_many_arguments)] mod dsproxyfactory_mod { diff --git a/ethers-middleware/tests/builder.rs b/ethers-middleware/tests/builder.rs index 7c988bb8..d99c603e 100644 --- a/ethers-middleware/tests/builder.rs +++ b/ethers-middleware/tests/builder.rs @@ -1,69 +1,67 @@ #![cfg(not(target_arch = "wasm32"))] -#[cfg(not(feature = "celo"))] -mod tests { - use ethers_core::{rand::thread_rng, types::U64}; - use ethers_middleware::{ - builder::MiddlewareBuilder, - gas_escalator::{Frequency, GasEscalatorMiddleware, GeometricGasPrice}, - gas_oracle::{EthGasStation, GasOracleMiddleware}, - nonce_manager::NonceManagerMiddleware, - signer::SignerMiddleware, - }; - use ethers_providers::{Middleware, Provider}; - use ethers_signers::{LocalWallet, Signer}; - #[tokio::test] - async fn build_raw_middleware_stack() { - let (provider, mock) = Provider::mocked(); +use ethers_core::{rand::thread_rng, types::U64}; +use ethers_middleware::{ + builder::MiddlewareBuilder, + gas_escalator::{Frequency, GasEscalatorMiddleware, GeometricGasPrice}, + gas_oracle::{GasNow, GasOracleMiddleware}, + nonce_manager::NonceManagerMiddleware, + signer::SignerMiddleware, +}; +use ethers_providers::{Middleware, Provider}; +use ethers_signers::{LocalWallet, Signer}; - let signer = LocalWallet::new(&mut thread_rng()); - let address = signer.address(); - let escalator = GeometricGasPrice::new(1.125, 60u64, None::); +#[tokio::test] +async fn build_raw_middleware_stack() { + let (provider, mock) = Provider::mocked(); - let provider = provider - .wrap_into(|p| GasEscalatorMiddleware::new(p, escalator, Frequency::PerBlock)) - .wrap_into(|p| GasOracleMiddleware::new(p, EthGasStation::new(None))) - .wrap_into(|p| SignerMiddleware::new(p, signer)) - .wrap_into(|p| NonceManagerMiddleware::new(p, address)); + let signer = LocalWallet::new(&mut thread_rng()); + let address = signer.address(); + let escalator = GeometricGasPrice::new(1.125, 60u64, None::); - // push a response - mock.push(U64::from(12u64)).unwrap(); - let block: U64 = provider.get_block_number().await.unwrap(); - assert_eq!(block.as_u64(), 12); + let provider = provider + .wrap_into(|p| GasEscalatorMiddleware::new(p, escalator, Frequency::PerBlock)) + .wrap_into(|p| GasOracleMiddleware::new(p, GasNow::new())) + .wrap_into(|p| SignerMiddleware::new(p, signer)) + .wrap_into(|p| NonceManagerMiddleware::new(p, address)); - provider.get_block_number().await.unwrap_err(); + // push a response + mock.push(U64::from(12u64)).unwrap(); + let block: U64 = provider.get_block_number().await.unwrap(); + assert_eq!(block.as_u64(), 12); - // 2 calls were made - mock.assert_request("eth_blockNumber", ()).unwrap(); - mock.assert_request("eth_blockNumber", ()).unwrap(); - mock.assert_request("eth_blockNumber", ()).unwrap_err(); - } + provider.get_block_number().await.unwrap_err(); - #[tokio::test] - async fn build_declarative_middleware_stack() { - let (provider, mock) = Provider::mocked(); - - let signer = LocalWallet::new(&mut thread_rng()); - let address = signer.address(); - let escalator = GeometricGasPrice::new(1.125, 60u64, None::); - let gas_oracle = EthGasStation::new(None); - - let provider = provider - .wrap_into(|p| GasEscalatorMiddleware::new(p, escalator, Frequency::PerBlock)) - .gas_oracle(gas_oracle) - .with_signer(signer) - .nonce_manager(address); - - // push a response - mock.push(U64::from(12u64)).unwrap(); - let block: U64 = provider.get_block_number().await.unwrap(); - assert_eq!(block.as_u64(), 12); - - provider.get_block_number().await.unwrap_err(); - - // 2 calls were made - mock.assert_request("eth_blockNumber", ()).unwrap(); - mock.assert_request("eth_blockNumber", ()).unwrap(); - mock.assert_request("eth_blockNumber", ()).unwrap_err(); - } + // 2 calls were made + mock.assert_request("eth_blockNumber", ()).unwrap(); + mock.assert_request("eth_blockNumber", ()).unwrap(); + mock.assert_request("eth_blockNumber", ()).unwrap_err(); +} + +#[tokio::test] +async fn build_declarative_middleware_stack() { + let (provider, mock) = Provider::mocked(); + + let signer = LocalWallet::new(&mut thread_rng()); + let address = signer.address(); + let escalator = GeometricGasPrice::new(1.125, 60u64, None::); + let gas_oracle = GasNow::new(); + + let provider = provider + .wrap_into(|p| GasEscalatorMiddleware::new(p, escalator, Frequency::PerBlock)) + .gas_oracle(gas_oracle) + .with_signer(signer) + .nonce_manager(address); + + // push a response + mock.push(U64::from(12u64)).unwrap(); + let block: U64 = provider.get_block_number().await.unwrap(); + assert_eq!(block.as_u64(), 12); + + provider.get_block_number().await.unwrap_err(); + + // 2 calls were made + mock.assert_request("eth_blockNumber", ()).unwrap(); + mock.assert_request("eth_blockNumber", ()).unwrap(); + mock.assert_request("eth_blockNumber", ()).unwrap_err(); } diff --git a/ethers-middleware/tests/gas_escalator.rs b/ethers-middleware/tests/gas_escalator.rs index a9eff59c..66d7d4ff 100644 --- a/ethers-middleware/tests/gas_escalator.rs +++ b/ethers-middleware/tests/gas_escalator.rs @@ -1,4 +1,5 @@ #![cfg(not(target_arch = "wasm32"))] + use ethers_core::types::*; use ethers_middleware::{ gas_escalator::{Frequency, GasEscalatorMiddleware, GeometricGasPrice}, @@ -12,6 +13,7 @@ use std::time::Duration; #[ignore] async fn gas_escalator_live() { // connect to ropsten for getting bad block times + #[allow(deprecated)] let provider = ethers_providers::ROPSTEN.ws().await; let provider = provider.interval(Duration::from_millis(2000u64)); let wallet = "fdb33e2105f08abe41a8ee3b758726a31abdd57b7a443f470f23efce853af169" @@ -33,7 +35,7 @@ async fn gas_escalator_live() { provider.send_transaction(tx.clone().nonce(nonce + 2), None).await.unwrap(); // Wait a bunch of seconds and refresh etherscan to see the transactions get bumped - tokio::time::sleep(std::time::Duration::from_secs(100)).await; + tokio::time::sleep(Duration::from_secs(100)).await; // TODO: Figure out how to test this behavior properly in a local network. If the gas price was // bumped then the tx hash will be different diff --git a/ethers-middleware/tests/gas_oracle.rs b/ethers-middleware/tests/gas_oracle.rs index 10db9ba0..9464fb3d 100644 --- a/ethers-middleware/tests/gas_oracle.rs +++ b/ethers-middleware/tests/gas_oracle.rs @@ -1,16 +1,14 @@ #![cfg(not(target_arch = "wasm32"))] -use std::convert::TryFrom; - use async_trait::async_trait; - use ethers_core::{types::*, utils::Anvil}; use ethers_middleware::gas_oracle::{ - EthGasStation, Etherchain, Etherscan, GasCategory, GasOracle, GasOracleError, - GasOracleMiddleware, + BlockNative, Etherchain, Etherscan, GasCategory, GasNow, GasOracle, GasOracleError, + GasOracleMiddleware, Polygon, ProviderOracle, Result, }; use ethers_providers::{Http, Middleware, Provider}; use serial_test::serial; +use std::convert::TryFrom; #[derive(Debug)] struct FakeGasOracle { @@ -20,17 +18,18 @@ struct FakeGasOracle { #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl GasOracle for FakeGasOracle { - async fn fetch(&self) -> Result { + async fn fetch(&self) -> Result { Ok(self.gas_price) } - async fn estimate_eip1559_fees(&self) -> Result<(U256, U256), GasOracleError> { + async fn estimate_eip1559_fees(&self) -> Result<(U256, U256)> { Err(GasOracleError::Eip1559EstimationNotSupported) } } #[tokio::test] -async fn using_gas_oracle() { +#[serial] +async fn provider_using_gas_oracle() { let anvil = Anvil::new().spawn(); let from = anvil.addresses()[0]; @@ -38,11 +37,11 @@ async fn using_gas_oracle() { // connect to the network let provider = Provider::::try_from(anvil.endpoint()).unwrap(); - // initial base fee - let base_fee = 1_000_000_000u64; // assign a gas oracle to use - let gas_oracle = FakeGasOracle { gas_price: (base_fee + 1337).into() }; - let expected_gas_price = gas_oracle.fetch().await.unwrap(); + let expected_gas_price = U256::from(1234567890_u64); + let gas_oracle = FakeGasOracle { gas_price: expected_gas_price }; + let gas_price = gas_oracle.fetch().await.unwrap(); + assert_eq!(gas_price, expected_gas_price); let provider = GasOracleMiddleware::new(provider, gas_oracle); @@ -55,35 +54,70 @@ async fn using_gas_oracle() { } #[tokio::test] -async fn eth_gas_station() { - // initialize and fetch gas estimates from EthGasStation - let eth_gas_station_oracle = EthGasStation::default(); - let data = eth_gas_station_oracle.fetch().await; - data.unwrap(); +#[serial] +async fn provider_oracle() { + // spawn anvil and connect to it + let anvil = Anvil::new().spawn(); + let provider = Provider::::try_from(anvil.endpoint()).unwrap(); + + // assert that provider.get_gas_price() and oracle.fetch() return the same value + let expected_gas_price = provider.get_gas_price().await.unwrap(); + let provider_oracle = ProviderOracle::new(provider); + let gas = provider_oracle.fetch().await.unwrap(); + assert_eq!(gas, expected_gas_price); +} + +#[tokio::test] +async fn blocknative() { + let gas_now_oracle = BlockNative::default(); + let gas_price = gas_now_oracle.fetch().await.unwrap(); + assert!(gas_price > U256::zero()); +} + +#[tokio::test] +#[ignore = "ETHGasStation is shutting down: https://twitter.com/ETHGasStation/status/1597341610777317376"] +#[allow(deprecated)] +async fn eth_gas_station() { + let eth_gas_station_oracle = ethers_middleware::gas_oracle::EthGasStation::default(); + let gas_price = eth_gas_station_oracle.fetch().await.unwrap(); + assert!(gas_price > U256::zero()); +} + +#[tokio::test] +#[ignore = "Etherchain / beaconcha.in's `gasPriceOracle` API currently returns 404: https://www.etherchain.org/api/gasPriceOracle"] +async fn etherchain() { + let etherchain_oracle = Etherchain::default(); + let gas_price = etherchain_oracle.fetch().await.unwrap(); + assert!(gas_price > U256::zero()); } #[tokio::test] -#[serial] async fn etherscan() { let etherscan_client = ethers_etherscan::Client::new_from_env(Chain::Mainnet).unwrap(); // initialize and fetch gas estimates from Etherscan // since etherscan does not support `fastest` category, we expect an error let etherscan_oracle = Etherscan::new(etherscan_client.clone()).category(GasCategory::Fastest); - let data = etherscan_oracle.fetch().await; - data.unwrap_err(); + let error = etherscan_oracle.fetch().await.unwrap_err(); + assert!(matches!(error, GasOracleError::GasCategoryNotSupported)); // but fetching the `standard` gas price should work fine let etherscan_oracle = Etherscan::new(etherscan_client).category(GasCategory::SafeLow); - let data = etherscan_oracle.fetch().await; - data.unwrap(); + let gas_price = etherscan_oracle.fetch().await.unwrap(); + assert!(gas_price > U256::zero()); } #[tokio::test] -async fn etherchain() { - // initialize and fetch gas estimates from Etherchain - let etherchain_oracle = Etherchain::default().category(GasCategory::Fast); - let data = etherchain_oracle.fetch().await; - data.unwrap(); +async fn gas_now() { + let gas_now_oracle = GasNow::default(); + let gas_price = gas_now_oracle.fetch().await.unwrap(); + assert!(gas_price > U256::zero()); +} + +#[tokio::test] +async fn polygon() { + let polygon_oracle = Polygon::default(); + let gas_price = polygon_oracle.fetch().await.unwrap(); + assert!(gas_price > U256::zero()); } diff --git a/ethers-middleware/tests/nonce_manager.rs b/ethers-middleware/tests/nonce_manager.rs index ce7bac22..1be27fec 100644 --- a/ethers-middleware/tests/nonce_manager.rs +++ b/ethers-middleware/tests/nonce_manager.rs @@ -1,18 +1,18 @@ -#![cfg(not(target_arch = "wasm32"))] -#[tokio::test] -#[cfg(not(feature = "celo"))] -async fn nonce_manager() { - use ethers_core::types::*; - use ethers_middleware::{nonce_manager::NonceManagerMiddleware, signer::SignerMiddleware}; - use ethers_providers::Middleware; - use ethers_signers::{LocalWallet, Signer}; - use std::time::Duration; +#![cfg(all(not(target_arch = "wasm32"), not(feature = "celo")))] - let provider = ethers_providers::GOERLI.provider().interval(Duration::from_millis(2000u64)); +use ethers_core::types::*; +use ethers_middleware::{nonce_manager::NonceManagerMiddleware, signer::SignerMiddleware}; +use ethers_providers::Middleware; +use ethers_signers::{LocalWallet, Signer}; +use std::time::Duration; + +#[tokio::test] +async fn nonce_manager() { + let provider = ethers_providers::GOERLI.provider().interval(Duration::from_millis(2000)); let chain_id = provider.get_chainid().await.unwrap().as_u64(); let wallet = std::env::var("GOERLI_PRIVATE_KEY") - .unwrap() + .expect("GOERLI_PRIVATE_KEY is not defined") .parse::() .unwrap() .with_chain_id(chain_id); @@ -44,12 +44,12 @@ async fn nonce_manager() { } // sleep a bit to ensure there's no flakiness in the test - std::thread::sleep(std::time::Duration::new(5, 0)); + tokio::time::sleep(Duration::from_secs(10)).await; let mut nonces = Vec::with_capacity(num_tx); for tx_hash in tx_hashes { nonces.push(provider.get_transaction(tx_hash).await.unwrap().unwrap().nonce.as_u64()); } - assert_eq!(nonces, (nonce..nonce + (num_tx as u64)).collect::>()) + assert_eq!(nonces, (nonce..nonce + num_tx as u64).collect::>()) } diff --git a/ethers-middleware/tests/signer.rs b/ethers-middleware/tests/signer.rs index a7c16a8c..eecd1e65 100644 --- a/ethers-middleware/tests/signer.rs +++ b/ethers-middleware/tests/signer.rs @@ -1,14 +1,22 @@ #![allow(unused)] -use ethers_providers::{Http, JsonRpcClient, Middleware, Provider, GOERLI}; +use ethers_contract::ContractFactory; use ethers_core::{ - types::{BlockNumber, TransactionRequest}, - utils::parse_units, + abi::Abi, + types::*, + utils::{parse_ether, parse_units, Anvil}, }; use ethers_middleware::signer::SignerMiddleware; +use ethers_providers::{Http, JsonRpcClient, Middleware, Provider, GOERLI}; use ethers_signers::{coins_bip39::English, LocalWallet, MnemonicBuilder, Signer}; +use ethers_solc::Solc; use once_cell::sync::Lazy; -use std::{convert::TryFrom, iter::Cycle, sync::atomic::AtomicU8, time::Duration}; +use std::{ + convert::TryFrom, + iter::Cycle, + sync::{atomic::AtomicU8, Arc}, + time::Duration, +}; static WALLETS: Lazy = Lazy::new(|| { TestWallets { @@ -22,8 +30,6 @@ static WALLETS: Lazy = Lazy::new(|| { #[tokio::test] #[cfg(not(feature = "celo"))] async fn send_eth() { - use ethers_core::utils::Anvil; - let anvil = Anvil::new().spawn(); // this private key belongs to the above mnemonic @@ -95,10 +101,6 @@ async fn pending_txs_with_confirmations_testnet() { generic_pending_txs_test(provider, address).await; } -#[cfg(not(feature = "celo"))] -use ethers_core::types::{Address, Eip1559TransactionRequest}; -use ethers_core::utils::parse_ether; - // different keys to avoid nonce errors #[tokio::test] #[cfg(not(feature = "celo"))] @@ -195,8 +197,6 @@ async fn test_send_transaction() { #[tokio::test] #[cfg(not(feature = "celo"))] async fn send_transaction_handles_tx_from_field() { - use ethers_core::utils::Anvil; - // launch anvil let anvil = Anvil::new().spawn(); @@ -240,14 +240,6 @@ async fn send_transaction_handles_tx_from_field() { #[tokio::test] #[cfg(feature = "celo")] async fn deploy_and_call_contract() { - use ethers_contract::ContractFactory; - use ethers_core::{ - abi::Abi, - types::{BlockNumber, Bytes, H256, U256}, - }; - use ethers_solc::Solc; - use std::sync::Arc; - // compiles the given contract and returns the ABI and Bytecode fn compile_contract(path: &str, name: &str) -> (Abi, Bytes) { let path = format!("./tests/solidity-contracts/{path}"); @@ -302,7 +294,7 @@ impl TestWallets { #[allow(unused)] pub async fn fund>(&self, provider: &Provider, n: U) { let addrs = (0..n.into()).map(|i| self.get(i).address()).collect::>(); - // hardcoded funder address private key, goerli + // hardcoded funder address private key, GOERLI let signer = "39aa18eeb5d12c071e5f19d8e9375a872e90cb1f2fa640384ffd8800a2f3e8f1" .parse::() .unwrap() diff --git a/ethers-middleware/tests/stack.rs b/ethers-middleware/tests/stack.rs index c7cb1ea2..bf70056f 100644 --- a/ethers-middleware/tests/stack.rs +++ b/ethers-middleware/tests/stack.rs @@ -1,95 +1,93 @@ -#![cfg(not(target_arch = "wasm32"))] -#[cfg(not(feature = "celo"))] -mod tests { - use ethers_core::{rand::thread_rng, types::TransactionRequest, utils::Anvil}; - use ethers_middleware::{ - gas_escalator::{Frequency, GasEscalatorMiddleware, GeometricGasPrice}, - gas_oracle::{EthGasStation, GasCategory, GasOracleMiddleware}, - nonce_manager::NonceManagerMiddleware, - signer::SignerMiddleware, - }; - use ethers_providers::{Http, Middleware, Provider}; - use ethers_signers::{LocalWallet, Signer}; - use std::convert::TryFrom; +#![cfg(all(not(target_arch = "wasm32"), not(feature = "celo")))] - #[tokio::test] - async fn mock_with_middleware() { - let (provider, mock) = Provider::mocked(); +use ethers_core::{rand::thread_rng, types::TransactionRequest, utils::Anvil}; +use ethers_middleware::{ + gas_escalator::{Frequency, GasEscalatorMiddleware, GeometricGasPrice}, + gas_oracle::{GasCategory, GasNow, GasOracleMiddleware}, + nonce_manager::NonceManagerMiddleware, + signer::SignerMiddleware, +}; +use ethers_providers::{Http, Middleware, Provider}; +use ethers_signers::{LocalWallet, Signer}; +use std::convert::TryFrom; - // add a bunch of middlewares - let gas_oracle = EthGasStation::new(None).category(GasCategory::SafeLow); - let signer = LocalWallet::new(&mut thread_rng()); - let address = signer.address(); - let escalator = GeometricGasPrice::new(1.125, 60u64, None::); - let provider = GasEscalatorMiddleware::new(provider, escalator, Frequency::PerBlock); - let provider = GasOracleMiddleware::new(provider, gas_oracle); - let provider = SignerMiddleware::new(provider, signer); - let provider = NonceManagerMiddleware::new(provider, address); +#[tokio::test] +async fn mock_with_middleware() { + let (provider, mock) = Provider::mocked(); - // push a response - use ethers_core::types::U64; - mock.push(U64::from(12u64)).unwrap(); - let blk = provider.get_block_number().await.unwrap(); - assert_eq!(blk.as_u64(), 12); + // add a bunch of middlewares + let gas_oracle = GasNow::new().category(GasCategory::SafeLow); + let signer = LocalWallet::new(&mut thread_rng()); + let address = signer.address(); + let escalator = GeometricGasPrice::new(1.125, 60u64, None::); + let provider = GasEscalatorMiddleware::new(provider, escalator, Frequency::PerBlock); + let provider = GasOracleMiddleware::new(provider, gas_oracle); + let provider = SignerMiddleware::new(provider, signer); + let provider = NonceManagerMiddleware::new(provider, address); - // now that the response is gone, there's nothing left - // TODO: This returns: - // MiddlewareError( - // MiddlewareError( - // MiddlewareError( - // MiddlewareError( - // JsonRpcClientError(EmptyResponses) - // )))) - // Can we flatten it in any way? Maybe inherent to the middleware - // infrastructure - provider.get_block_number().await.unwrap_err(); + // push a response + use ethers_core::types::U64; + mock.push(U64::from(12u64)).unwrap(); + let blk = provider.get_block_number().await.unwrap(); + assert_eq!(blk.as_u64(), 12); - // 2 calls were made - mock.assert_request("eth_blockNumber", ()).unwrap(); - mock.assert_request("eth_blockNumber", ()).unwrap(); - mock.assert_request("eth_blockNumber", ()).unwrap_err(); - } + // now that the response is gone, there's nothing left + // TODO: This returns: + // MiddlewareError( + // MiddlewareError( + // MiddlewareError( + // MiddlewareError( + // JsonRpcClientError(EmptyResponses) + // )))) + // Can we flatten it in any way? Maybe inherent to the middleware + // infrastructure + provider.get_block_number().await.unwrap_err(); - #[tokio::test] - async fn can_stack_middlewares() { - let anvil = Anvil::new().block_time(5u64).spawn(); - let gas_oracle = EthGasStation::new(None).category(GasCategory::SafeLow); - let signer: LocalWallet = anvil.keys()[0].clone().into(); - let address = signer.address(); - - // the base provider - let provider = Arc::new(Provider::::try_from(anvil.endpoint()).unwrap()); - let chain_id = provider.get_chainid().await.unwrap().as_u64(); - let signer = signer.with_chain_id(chain_id); - - // the Gas Price escalator middleware is the first middleware above the provider, - // so that it receives the transaction last, after all the other middleware - // have modified it accordingly - let escalator = GeometricGasPrice::new(1.125, 60u64, None::); - let provider = GasEscalatorMiddleware::new(provider, escalator, Frequency::PerBlock); - - // The gas price middleware MUST be below the signing middleware for things to work - let provider = GasOracleMiddleware::new(provider, gas_oracle); - - // The signing middleware signs txs - use std::sync::Arc; - let provider = Arc::new(SignerMiddleware::new(provider, signer)); - - // The nonce manager middleware MUST be above the signing middleware so that it overrides - // the nonce and the signer does not make any eth_getTransaction count calls - let provider = NonceManagerMiddleware::new(provider, address); - - let tx = TransactionRequest::new(); - let mut pending_txs = Vec::new(); - for _ in 0..10 { - let pending = provider.send_transaction(tx.clone(), None).await.unwrap(); - let hash = *pending; - let gas_price = provider.get_transaction(hash).await.unwrap().unwrap().gas_price; - dbg!(gas_price); - pending_txs.push(pending); - } - - let receipts = futures_util::future::join_all(pending_txs); - dbg!(receipts.await); - } + // 2 calls were made + mock.assert_request("eth_blockNumber", ()).unwrap(); + mock.assert_request("eth_blockNumber", ()).unwrap(); + mock.assert_request("eth_blockNumber", ()).unwrap_err(); +} + +#[tokio::test] +async fn can_stack_middlewares() { + let anvil = Anvil::new().block_time(5u64).spawn(); + let gas_oracle = GasNow::new().category(GasCategory::SafeLow); + let signer: LocalWallet = anvil.keys()[0].clone().into(); + let address = signer.address(); + + // the base provider + let provider = Arc::new(Provider::::try_from(anvil.endpoint()).unwrap()); + let chain_id = provider.get_chainid().await.unwrap().as_u64(); + let signer = signer.with_chain_id(chain_id); + + // the Gas Price escalator middleware is the first middleware above the provider, + // so that it receives the transaction last, after all the other middleware + // have modified it accordingly + let escalator = GeometricGasPrice::new(1.125, 60u64, None::); + let provider = GasEscalatorMiddleware::new(provider, escalator, Frequency::PerBlock); + + // The gas price middleware MUST be below the signing middleware for things to work + let provider = GasOracleMiddleware::new(provider, gas_oracle); + + // The signing middleware signs txs + use std::sync::Arc; + let provider = Arc::new(SignerMiddleware::new(provider, signer)); + + // The nonce manager middleware MUST be above the signing middleware so that it overrides + // the nonce and the signer does not make any eth_getTransaction count calls + let provider = NonceManagerMiddleware::new(provider, address); + + let tx = TransactionRequest::new(); + let mut pending_txs = Vec::new(); + for _ in 0..10 { + let pending = provider.send_transaction(tx.clone(), None).await.unwrap(); + let hash = *pending; + let gas_price = provider.get_transaction(hash).await.unwrap().unwrap().gas_price; + dbg!(gas_price); + pending_txs.push(pending); + } + + let receipts = futures_util::future::join_all(pending_txs); + dbg!(receipts.await); } diff --git a/ethers-middleware/tests/transformer.rs b/ethers-middleware/tests/transformer.rs index f27ef7f0..c4b37c0a 100644 --- a/ethers-middleware/tests/transformer.rs +++ b/ethers-middleware/tests/transformer.rs @@ -1,5 +1,5 @@ -#![cfg(not(target_arch = "wasm32"))] -#![allow(unused)] +#![cfg(all(not(target_arch = "wasm32"), not(feature = "celo")))] + use ethers_contract::{BaseContract, ContractFactory}; use ethers_core::{abi::Abi, types::*, utils::Anvil}; use ethers_middleware::{ @@ -24,7 +24,6 @@ fn compile_contract(path: &str, name: &str) -> (Abi, Bytes) { } #[tokio::test] -#[cfg(not(feature = "celo"))] async fn ds_proxy_transformer() { // randomness let mut rng = rand::thread_rng(); @@ -83,7 +82,6 @@ async fn ds_proxy_transformer() { } #[tokio::test] -#[cfg(not(feature = "celo"))] async fn ds_proxy_code() { // randomness let mut rng = rand::thread_rng(); diff --git a/ethers-providers/Cargo.toml b/ethers-providers/Cargo.toml index d11f4f81..e3117d68 100644 --- a/ethers-providers/Cargo.toml +++ b/ethers-providers/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ethers-providers" version = "1.0.2" -edition = "2018" +edition = "2021" rust-version = "1.64" authors = ["Georgios Konstantopoulos "] license = "MIT OR Apache-2.0" @@ -43,7 +43,7 @@ tracing = { version = "0.1.37", default-features = false } tracing-futures = { version = "0.2.5", default-features = false, features = ["std-future"] } bytes = { version = "1.3.0", default-features = false, optional = true } -once_cell = "1.16.0" +once_cell = "1.17.0" hashers = "1.0.1" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/ethers-providers/src/lib.rs b/ethers-providers/src/lib.rs index 2c0ba7f6..31ef49fe 100644 --- a/ethers-providers/src/lib.rs +++ b/ethers-providers/src/lib.rs @@ -540,6 +540,7 @@ pub trait Middleware: Sync + Send + Debug { } // Geth `trace` support + /// After replaying any previous transactions in the same block, /// Replays a transaction, returning the traces configured with passed options async fn debug_trace_transaction( @@ -550,6 +551,16 @@ pub trait Middleware: Sync + Send + Debug { self.inner().debug_trace_transaction(tx_hash, trace_options).await.map_err(FromErr::from) } + /// Executes the given call and returns a number of possible traces for it + async fn debug_trace_call + Send + Sync>( + &self, + req: T, + block: Option, + trace_options: GethDebugTracingCallOptions, + ) -> Result { + self.inner().debug_trace_call(req, block, trace_options).await.map_err(FromErr::from) + } + // Parity `trace` support /// Executes the given call and returns a number of possible traces for it @@ -712,7 +723,8 @@ pub trait CeloMiddleware: Middleware { } } -pub use test_provider::{GOERLI, MAINNET, ROPSTEN}; +#[allow(deprecated)] +pub use test_provider::{GOERLI, MAINNET, ROPSTEN, SEPOLIA}; /// Pre-instantiated Infura HTTP clients which rotate through multiple API keys /// to prevent rate limits @@ -732,9 +744,13 @@ pub mod test_provider { "5c812e02193c4ba793f8c214317582bd", ]; - pub static GOERLI: Lazy = Lazy::new(|| TestProvider::new(INFURA_KEYS, "goerli")); pub static MAINNET: Lazy = Lazy::new(|| TestProvider::new(INFURA_KEYS, "mainnet")); + pub static GOERLI: Lazy = Lazy::new(|| TestProvider::new(INFURA_KEYS, "goerli")); + pub static SEPOLIA: Lazy = + Lazy::new(|| TestProvider::new(INFURA_KEYS, "sepolia")); + + #[deprecated = "Ropsten testnet has been deprecated in favor of Goerli or Sepolia."] pub static ROPSTEN: Lazy = Lazy::new(|| TestProvider::new(INFURA_KEYS, "ropsten")); @@ -745,16 +761,14 @@ pub mod test_provider { } impl TestProvider { - pub fn new(keys: &'static [&'static str], network: &str) -> Self { - Self { keys: Mutex::new(keys.iter().cycle()), network: network.to_owned() } + pub fn new(keys: &'static [&'static str], network: impl Into) -> Self { + Self { keys: keys.iter().cycle().into(), network: network.into() } } pub fn url(&self) -> String { - format!( - "https://{}.infura.io/v3/{}", - self.network, - self.keys.lock().unwrap().next().unwrap() - ) + let Self { network, keys } = self; + let key = keys.lock().unwrap().next().unwrap(); + format!("https://{network}.infura.io/v3/{key}") } pub fn provider(&self) -> Provider { diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs index 71cf41ce..97d5a995 100644 --- a/ethers-providers/src/provider.rs +++ b/ethers-providers/src/provider.rs @@ -22,9 +22,10 @@ use ethers_core::{ types::{ transaction::{eip2718::TypedTransaction, eip2930::AccessListWithGasUsed}, Address, Block, BlockId, BlockNumber, BlockTrace, Bytes, EIP1186ProofResponse, FeeHistory, - Filter, FilterBlockOption, GethDebugTracingOptions, GethTrace, Log, NameOrAddress, - Selector, Signature, Trace, TraceFilter, TraceType, Transaction, TransactionReceipt, - TransactionRequest, TxHash, TxpoolContent, TxpoolInspect, TxpoolStatus, H256, U256, U64, + Filter, FilterBlockOption, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, + Log, NameOrAddress, Selector, Signature, Trace, TraceFilter, TraceType, Transaction, + TransactionReceipt, TransactionRequest, TxHash, TxpoolContent, TxpoolInspect, TxpoolStatus, + H256, U256, U64, }, utils, }; @@ -351,8 +352,19 @@ impl Middleware for Provider

{ if inner.max_fee_per_gas.is_none() || inner.max_priority_fee_per_gas.is_none() { let (max_fee_per_gas, max_priority_fee_per_gas) = self.estimate_eip1559_fees(None).await?; - inner.max_fee_per_gas = Some(max_fee_per_gas); - inner.max_priority_fee_per_gas = Some(max_priority_fee_per_gas); + // we want to avoid overriding the user if either of these + // are set. In order to do this, we refuse to override the + // `max_fee_per_gas` if already set. + // However, we must preserve the constraint that the tip + // cannot be higher than max fee, so we override user + // intent if that is so. We override by + // - first: if set, set to the min(current value, MFPG) + // - second, if still unset, use the RPC estimated amount + let mfpg = inner.max_fee_per_gas.get_or_insert(max_fee_per_gas); + inner + .max_priority_fee_per_gas + .map(|tip| std::cmp::min(tip, *mfpg)) + .get_or_insert(max_priority_fee_per_gas); }; } } @@ -1048,6 +1060,20 @@ impl Middleware for Provider

{ self.request("debug_traceTransaction", [tx_hash, trace_options]).await } + /// Executes the given call and returns a number of possible traces for it + async fn debug_trace_call + Send + Sync>( + &self, + req: T, + block: Option, + trace_options: GethDebugTracingCallOptions, + ) -> Result { + let req = req.into(); + let req = utils::serialize(&req); + let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into())); + let trace_options = utils::serialize(&trace_options); + self.request("debug_traceCall", [req, block, trace_options]).await + } + /// Executes the given call and returns a number of possible traces for it async fn trace_call + Send + Sync>( &self, diff --git a/ethers-providers/src/transports/rw.rs b/ethers-providers/src/transports/rw.rs index f54a96eb..58499d20 100644 --- a/ethers-providers/src/transports/rw.rs +++ b/ethers-providers/src/transports/rw.rs @@ -9,7 +9,7 @@ use serde::{de::DeserializeOwned, Serialize}; use thiserror::Error; -/// A client contains two clients. +/// A client containing two clients. /// /// One is used for _read_ operations /// One is used for _write_ operations that consume gas `["eth_sendTransaction", @@ -48,7 +48,7 @@ impl RwClient { &self.r } - /// Returns the client used for read operations + /// Returns the client used for write operations pub fn write_client(&self) -> &Write { &self.w } diff --git a/ethers-signers/Cargo.toml b/ethers-signers/Cargo.toml index 96915bf4..8a25e34f 100644 --- a/ethers-signers/Cargo.toml +++ b/ethers-signers/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ethers-signers" version = "1.0.2" -edition = "2018" +edition = "2021" rust-version = "1.64" authors = ["Georgios Konstantopoulos "] license = "MIT OR Apache-2.0" @@ -16,7 +16,7 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] ethers-core = { version = "^1.0.0", path = "../ethers-core", features = ["eip712"] } -thiserror = { version = "1.0.37", default-features = false } +thiserror = { version = "1.0.38", default-features = false } coins-bip32 = "0.7.0" coins-bip39 = "0.7.0" coins-ledger = { version = "0.7.0", default-features = false, optional = true } @@ -28,7 +28,7 @@ rand = { version = "0.8.5", default-features = false } yubihsm = { version = "0.41.0", features = ["secp256k1", "http", "usb"], optional = true } futures-util = { version = "^0.3", optional = true } futures-executor = { version = "^0.3", optional = true } -semver = { version = "1.0.14", optional = true } +semver = { version = "1.0.16", optional = true } trezor-client = { version = "0.0.7", optional = true, default-features = false, features = [ "f_ethereum", ] } diff --git a/ethers-signers/src/wallet/private_key.rs b/ethers-signers/src/wallet/private_key.rs index cdddf00a..ed10949c 100644 --- a/ethers-signers/src/wallet/private_key.rs +++ b/ethers-signers/src/wallet/private_key.rs @@ -90,6 +90,13 @@ impl Wallet { let address = secret_key_to_address(&signer); Self { signer, address, chain_id: 1 } } + + /// Creates a new Wallet instance from a raw scalar value (big endian). + pub fn from_bytes(bytes: &[u8]) -> Result { + let signer = SigningKey::from_bytes(bytes)?; + let address = secret_key_to_address(&signer); + Ok(Self { signer, address, chain_id: 1 }) + } } impl PartialEq for Wallet { @@ -305,4 +312,17 @@ mod tests { Address::from_str("6813Eb9362372EEF6200f3b1dbC3f819671cBA69").expect("Decoding failed") ); } + + #[test] + fn key_from_bytes() { + let wallet: Wallet = + "0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap(); + + let key_as_bytes = wallet.signer.to_bytes(); + let wallet_from_bytes = Wallet::from_bytes(&key_as_bytes).unwrap(); + + assert_eq!(wallet.address, wallet_from_bytes.address); + assert_eq!(wallet.chain_id, wallet_from_bytes.chain_id); + assert_eq!(wallet.signer, wallet_from_bytes.signer); + } } diff --git a/ethers-solc/Cargo.toml b/ethers-solc/Cargo.toml index 32b0f99d..3555e1b4 100644 --- a/ethers-solc/Cargo.toml +++ b/ethers-solc/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ethers-solc" version = "1.0.2" -edition = "2018" +edition = "2021" rust-version = "1.64" authors = [ "Matthias Seitz ", @@ -19,11 +19,11 @@ keywords = ["ethereum", "web3", "solc", "solidity", "ethers"] ethers-core = { version = "^1.0.0", path = "../ethers-core", default-features = false } serde_json = "1.0.68" serde = { version = "1.0.130", features = ["derive"] } -semver = { version = "1.0.14", features = ["serde"] } +semver = { version = "1.0.16", features = ["serde"] } walkdir = "2.3.2" tokio = { version = "1.18", default-features = false, features = ["rt"] } futures-util = { version = "^0.3", optional = true } -once_cell = "1.16.0" +once_cell = "1.17.0" regex = "1.7.0" md-5 = "0.10.5" thiserror = "1.0" @@ -31,7 +31,7 @@ hex = "0.4.3" yansi = "0.5.1" glob = "0.3.0" tracing = "0.1.37" -num_cpus = "1.14.0" +num_cpus = "1.15.0" tiny-keccak = { version = "2.0.2", default-features = false } tempfile = { version = "3.3.0", optional = true } fs_extra = { version = "1.2.0", optional = true } @@ -62,7 +62,7 @@ rand = "0.8.5" pretty_assertions = "1.3.0" tempfile = "3.3.0" tokio = { version = "1.18", features = ["full"] } -serde_path_to_error = "0.1.8" +serde_path_to_error = "0.1.9" [[bench]] name = "compile_many" diff --git a/ethers-solc/benches/compile_many.rs b/ethers-solc/benches/compile_many.rs index b667a6b9..b0df3c7a 100644 --- a/ethers-solc/benches/compile_many.rs +++ b/ethers-solc/benches/compile_many.rs @@ -36,7 +36,6 @@ fn load_compiler_inputs() -> Vec { let mut inputs = Vec::new(); for file in std::fs::read_dir(Path::new(&env!("CARGO_MANIFEST_DIR")).join("test-data/in")) .unwrap() - .into_iter() .take(5) { let file = file.unwrap(); diff --git a/ethers-solc/src/artifacts/ast/mod.rs b/ethers-solc/src/artifacts/ast/mod.rs index 79fd230a..730711ac 100644 --- a/ethers-solc/src/artifacts/ast/mod.rs +++ b/ethers-solc/src/artifacts/ast/mod.rs @@ -1086,11 +1086,11 @@ mod tests { let result: Result = serde_path_to_error::deserialize(deserializer); match result { Err(e) => { - println!("... {} fail: {e}", path_str); + println!("... {path_str} fail: {e}"); panic!(); } Ok(_) => { - println!("... {} ok", path_str); + println!("... {path_str} ok"); } } }) diff --git a/ethers-solc/src/artifacts/mod.rs b/ethers-solc/src/artifacts/mod.rs index 121479eb..1ab55662 100644 --- a/ethers-solc/src/artifacts/mod.rs +++ b/ethers-solc/src/artifacts/mod.rs @@ -26,7 +26,10 @@ pub mod contract; pub mod output_selection; pub mod serde_helpers; use crate::{ - artifacts::output_selection::{ContractOutputSelection, OutputSelection}, + artifacts::{ + lowfidelity::NodeType, + output_selection::{ContractOutputSelection, OutputSelection}, + }, filter::FilteredSources, }; pub use bytecode::*; diff --git a/ethers-solc/src/compile/mod.rs b/ethers-solc/src/compile/mod.rs index fe40cfb9..b34913fe 100644 --- a/ethers-solc/src/compile/mod.rs +++ b/ethers-solc/src/compile/mod.rs @@ -457,7 +457,7 @@ impl Solc { use sha2::Digest; let mut hasher = sha2::Sha256::new(); - hasher.update(&content); + hasher.update(content); let checksum_calc = &hasher.finalize()[..]; let checksum_found = &RELEASES.0.get_checksum(&version).expect("checksum not found"); diff --git a/ethers-solc/src/resolver/mod.rs b/ethers-solc/src/resolver/mod.rs index eceba2e1..848713ad 100644 --- a/ethers-solc/src/resolver/mod.rs +++ b/ethers-solc/src/resolver/mod.rs @@ -649,7 +649,7 @@ impl Graph { return Vec::new() } - let mut result = sets.pop().cloned().expect("not empty; qed.").clone(); + let mut result = sets.pop().cloned().expect("not empty; qed."); if !sets.is_empty() { result.retain(|item| sets.iter().all(|set| set.contains(item))); } diff --git a/ethers-solc/test-data/ast/ast-erc4626.json b/ethers-solc/test-data/ast/ast-erc4626.json new file mode 100644 index 00000000..7f677053 --- /dev/null +++ b/ethers-solc/test-data/ast/ast-erc4626.json @@ -0,0 +1,6612 @@ +{ + "absolutePath": "/home/oliver/Projects/github/rari-capital/solmate/src/mixins/ERC4626.sol", + "id": 2954, + "exportedSymbols": { + "ERC20": [ + 31408 + ], + "ERC4626": [ + 2953 + ], + "FixedPointMathLib": [ + 32321 + ], + "SafeTransferLib": [ + 32785 + ] + }, + "nodeType": "SourceUnit", + "src": "42:6474:4", + "nodes": [ + { + "id": 2434, + "nodeType": "PragmaDirective", + "src": "42:24:4", + "literals": [ + "solidity", + ">=", + "0.8", + ".0" + ] + }, + { + "id": 2436, + "nodeType": "ImportDirective", + "src": "68:42:4", + "absolutePath": "/home/oliver/Projects/github/rari-capital/solmate/src/tokens/ERC20.sol", + "file": "../tokens/ERC20.sol", + "nameLocation": "-1:-1:-1", + "scope": 2954, + "sourceUnit": 31409, + "symbolAliases": [ + { + "foreign": { + "id": 2435, + "name": "ERC20", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "src": "76:5:4", + "typeDescriptions": {} + }, + "nameLocation": "-1:-1:-1" + } + ], + "unitAlias": "" + }, + { + "id": 2438, + "nodeType": "ImportDirective", + "src": "111:61:4", + "absolutePath": "/home/oliver/Projects/github/rari-capital/solmate/src/utils/SafeTransferLib.sol", + "file": "../utils/SafeTransferLib.sol", + "nameLocation": "-1:-1:-1", + "scope": 2954, + "sourceUnit": 32786, + "symbolAliases": [ + { + "foreign": { + "id": 2437, + "name": "SafeTransferLib", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "src": "119:15:4", + "typeDescriptions": {} + }, + "nameLocation": "-1:-1:-1" + } + ], + "unitAlias": "" + }, + { + "id": 2440, + "nodeType": "ImportDirective", + "src": "173:65:4", + "absolutePath": "/home/oliver/Projects/github/rari-capital/solmate/src/utils/FixedPointMathLib.sol", + "file": "../utils/FixedPointMathLib.sol", + "nameLocation": "-1:-1:-1", + "scope": 2954, + "sourceUnit": 32322, + "symbolAliases": [ + { + "foreign": { + "id": 2439, + "name": "FixedPointMathLib", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "src": "181:17:4", + "typeDescriptions": {} + }, + "nameLocation": "-1:-1:-1" + } + ], + "unitAlias": "" + }, + { + "id": 2953, + "nodeType": "ContractDefinition", + "src": "395:6120:4", + "nodes": [ + { + "id": 2447, + "nodeType": "UsingForDirective", + "src": "436:32:4", + "libraryName": { + "id": 2444, + "name": "SafeTransferLib", + "nodeType": "IdentifierPath", + "referencedDeclaration": 32785, + "src": "442:15:4" + }, + "typeName": { + "id": 2446, + "nodeType": "UserDefinedTypeName", + "pathNode": { + "id": 2445, + "name": "ERC20", + "nodeType": "IdentifierPath", + "referencedDeclaration": 31408, + "src": "462:5:4" + }, + "referencedDeclaration": 31408, + "src": "462:5:4", + "typeDescriptions": { + "typeIdentifier": "t_contract$_ERC20_$31408", + "typeString": "contract ERC20" + } + } + }, + { + "id": 2450, + "nodeType": "UsingForDirective", + "src": "473:36:4", + "libraryName": { + "id": 2448, + "name": "FixedPointMathLib", + "nodeType": "IdentifierPath", + "referencedDeclaration": 32321, + "src": "479:17:4" + }, + "typeName": { + "id": 2449, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "501:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + }, + { + "id": 2460, + "nodeType": "EventDefinition", + "src": "694:93:4", + "anonymous": false, + "name": "Deposit", + "nameLocation": "700:7:4", + "parameters": { + "id": 2459, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2452, + "indexed": true, + "mutability": "mutable", + "name": "caller", + "nameLocation": "724:6:4", + "nodeType": "VariableDeclaration", + "scope": 2460, + "src": "708:22:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 2451, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "708:7:4", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 2454, + "indexed": true, + "mutability": "mutable", + "name": "owner", + "nameLocation": "748:5:4", + "nodeType": "VariableDeclaration", + "scope": 2460, + "src": "732:21:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 2453, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "732:7:4", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 2456, + "indexed": false, + "mutability": "mutable", + "name": "assets", + "nameLocation": "763:6:4", + "nodeType": "VariableDeclaration", + "scope": 2460, + "src": "755:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2455, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "755:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 2458, + "indexed": false, + "mutability": "mutable", + "name": "shares", + "nameLocation": "779:6:4", + "nodeType": "VariableDeclaration", + "scope": 2460, + "src": "771:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2457, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "771:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "707:79:4" + } + }, + { + "id": 2472, + "nodeType": "EventDefinition", + "src": "793:166:4", + "anonymous": false, + "name": "Withdraw", + "nameLocation": "799:8:4", + "parameters": { + "id": 2471, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2462, + "indexed": true, + "mutability": "mutable", + "name": "caller", + "nameLocation": "833:6:4", + "nodeType": "VariableDeclaration", + "scope": 2472, + "src": "817:22:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 2461, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "817:7:4", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 2464, + "indexed": true, + "mutability": "mutable", + "name": "receiver", + "nameLocation": "865:8:4", + "nodeType": "VariableDeclaration", + "scope": 2472, + "src": "849:24:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 2463, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "849:7:4", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 2466, + "indexed": true, + "mutability": "mutable", + "name": "owner", + "nameLocation": "899:5:4", + "nodeType": "VariableDeclaration", + "scope": 2472, + "src": "883:21:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 2465, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "883:7:4", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 2468, + "indexed": false, + "mutability": "mutable", + "name": "assets", + "nameLocation": "922:6:4", + "nodeType": "VariableDeclaration", + "scope": 2472, + "src": "914:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2467, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "914:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 2470, + "indexed": false, + "mutability": "mutable", + "name": "shares", + "nameLocation": "946:6:4", + "nodeType": "VariableDeclaration", + "scope": 2472, + "src": "938:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2469, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "938:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "807:151:4" + } + }, + { + "id": 2475, + "nodeType": "VariableDeclaration", + "src": "1146:28:4", + "constant": false, + "functionSelector": "38d52e0f", + "mutability": "immutable", + "name": "asset", + "nameLocation": "1169:5:4", + "scope": 2953, + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_contract$_ERC20_$31408", + "typeString": "contract ERC20" + }, + "typeName": { + "id": 2474, + "nodeType": "UserDefinedTypeName", + "pathNode": { + "id": 2473, + "name": "ERC20", + "nodeType": "IdentifierPath", + "referencedDeclaration": 31408, + "src": "1146:5:4" + }, + "referencedDeclaration": 31408, + "src": "1146:5:4", + "typeDescriptions": { + "typeIdentifier": "t_contract$_ERC20_$31408", + "typeString": "contract ERC20" + } + }, + "visibility": "public" + }, + { + "id": 2497, + "nodeType": "FunctionDefinition", + "src": "1181:172:4", + "body": { + "id": 2496, + "nodeType": "Block", + "src": "1322:31:4", + "statements": [ + { + "expression": { + "id": 2494, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "id": 2492, + "name": "asset", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2475, + "src": "1332:5:4", + "typeDescriptions": { + "typeIdentifier": "t_contract$_ERC20_$31408", + "typeString": "contract ERC20" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "id": 2493, + "name": "_asset", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2478, + "src": "1340:6:4", + "typeDescriptions": { + "typeIdentifier": "t_contract$_ERC20_$31408", + "typeString": "contract ERC20" + } + }, + "src": "1332:14:4", + "typeDescriptions": { + "typeIdentifier": "t_contract$_ERC20_$31408", + "typeString": "contract ERC20" + } + }, + "id": 2495, + "nodeType": "ExpressionStatement", + "src": "1332:14:4" + } + ] + }, + "implemented": true, + "kind": "constructor", + "modifiers": [ + { + "arguments": [ + { + "id": 2485, + "name": "_name", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2480, + "src": "1287:5:4", + "typeDescriptions": { + "typeIdentifier": "t_string_memory_ptr", + "typeString": "string memory" + } + }, + { + "id": 2486, + "name": "_symbol", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2482, + "src": "1294:7:4", + "typeDescriptions": { + "typeIdentifier": "t_string_memory_ptr", + "typeString": "string memory" + } + }, + { + "arguments": [], + "expression": { + "argumentTypes": [], + "expression": { + "id": 2487, + "name": "_asset", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2478, + "src": "1303:6:4", + "typeDescriptions": { + "typeIdentifier": "t_contract$_ERC20_$31408", + "typeString": "contract ERC20" + } + }, + "id": 2488, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "decimals", + "nodeType": "MemberAccess", + "referencedDeclaration": 31045, + "src": "1303:15:4", + "typeDescriptions": { + "typeIdentifier": "t_function_external_view$__$returns$_t_uint8_$", + "typeString": "function () view external returns (uint8)" + } + }, + "id": 2489, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "1303:17:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_uint8", + "typeString": "uint8" + } + } + ], + "id": 2490, + "kind": "baseConstructorSpecifier", + "modifierName": { + "id": 2484, + "name": "ERC20", + "nodeType": "IdentifierPath", + "referencedDeclaration": 31408, + "src": "1281:5:4" + }, + "nodeType": "ModifierInvocation", + "src": "1281:40:4" + } + ], + "name": "", + "nameLocation": "-1:-1:-1", + "parameters": { + "id": 2483, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2478, + "mutability": "mutable", + "name": "_asset", + "nameLocation": "1208:6:4", + "nodeType": "VariableDeclaration", + "scope": 2497, + "src": "1202:12:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_contract$_ERC20_$31408", + "typeString": "contract ERC20" + }, + "typeName": { + "id": 2477, + "nodeType": "UserDefinedTypeName", + "pathNode": { + "id": 2476, + "name": "ERC20", + "nodeType": "IdentifierPath", + "referencedDeclaration": 31408, + "src": "1202:5:4" + }, + "referencedDeclaration": 31408, + "src": "1202:5:4", + "typeDescriptions": { + "typeIdentifier": "t_contract$_ERC20_$31408", + "typeString": "contract ERC20" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 2480, + "mutability": "mutable", + "name": "_name", + "nameLocation": "1238:5:4", + "nodeType": "VariableDeclaration", + "scope": 2497, + "src": "1224:19:4", + "stateVariable": false, + "storageLocation": "memory", + "typeDescriptions": { + "typeIdentifier": "t_string_memory_ptr", + "typeString": "string" + }, + "typeName": { + "id": 2479, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "1224:6:4", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 2482, + "mutability": "mutable", + "name": "_symbol", + "nameLocation": "1267:7:4", + "nodeType": "VariableDeclaration", + "scope": 2497, + "src": "1253:21:4", + "stateVariable": false, + "storageLocation": "memory", + "typeDescriptions": { + "typeIdentifier": "t_string_memory_ptr", + "typeString": "string" + }, + "typeName": { + "id": 2481, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "1253:6:4", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "visibility": "internal" + } + ], + "src": "1192:88:4" + }, + "returnParameters": { + "id": 2491, + "nodeType": "ParameterList", + "parameters": [], + "src": "1322:0:4" + }, + "scope": 2953, + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "internal" + }, + { + "id": 2549, + "nodeType": "FunctionDefinition", + "src": "1547:516:4", + "body": { + "id": 2548, + "nodeType": "Block", + "src": "1638:425:4", + "statements": [ + { + "expression": { + "arguments": [ + { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 2514, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "components": [ + { + "id": 2511, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "id": 2507, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2504, + "src": "1732:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "arguments": [ + { + "id": 2509, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2499, + "src": "1756:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2508, + "name": "previewDeposit", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2822, + "src": "1741:14:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$_t_uint256_$returns$_t_uint256_$", + "typeString": "function (uint256) view returns (uint256)" + } + }, + "id": 2510, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "1741:22:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "1732:31:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "id": 2512, + "isConstant": false, + "isInlineArray": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "TupleExpression", + "src": "1731:33:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": "!=", + "rightExpression": { + "hexValue": "30", + "id": 2513, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1768:1:4", + "typeDescriptions": { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + }, + "value": "0" + }, + "src": "1731:38:4", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + { + "hexValue": "5a45524f5f534841524553", + "id": 2515, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1771:13:4", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_2119bd5d92259e418533f96b824fbd100e3dea453e6ac4c5f7315e6344368f2f", + "typeString": "literal_string \"ZERO_SHARES\"" + }, + "value": "ZERO_SHARES" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + { + "typeIdentifier": "t_stringliteral_2119bd5d92259e418533f96b824fbd100e3dea453e6ac4c5f7315e6344368f2f", + "typeString": "literal_string \"ZERO_SHARES\"" + } + ], + "id": 2506, + "name": "require", + "nodeType": "Identifier", + "overloadedDeclarations": [ + -18, + -18 + ], + "referencedDeclaration": -18, + "src": "1723:7:4", + "typeDescriptions": { + "typeIdentifier": "t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$", + "typeString": "function (bool,string memory) pure" + } + }, + "id": 2516, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "1723:62:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2517, + "nodeType": "ExpressionStatement", + "src": "1723:62:4" + }, + { + "expression": { + "arguments": [ + { + "expression": { + "id": 2521, + "name": "msg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -15, + "src": "1888:3:4", + "typeDescriptions": { + "typeIdentifier": "t_magic_message", + "typeString": "msg" + } + }, + "id": 2522, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "sender", + "nodeType": "MemberAccess", + "src": "1888:10:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "arguments": [ + { + "id": 2525, + "name": "this", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -28, + "src": "1908:4:4", + "typeDescriptions": { + "typeIdentifier": "t_contract$_ERC4626_$2953", + "typeString": "contract ERC4626" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_contract$_ERC4626_$2953", + "typeString": "contract ERC4626" + } + ], + "id": 2524, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "nodeType": "ElementaryTypeNameExpression", + "src": "1900:7:4", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_address_$", + "typeString": "type(address)" + }, + "typeName": { + "id": 2523, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "1900:7:4", + "typeDescriptions": {} + } + }, + "id": 2526, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "typeConversion", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "1900:13:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2527, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2499, + "src": "1915:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "expression": { + "id": 2518, + "name": "asset", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2475, + "src": "1865:5:4", + "typeDescriptions": { + "typeIdentifier": "t_contract$_ERC20_$31408", + "typeString": "contract ERC20" + } + }, + "id": 2520, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "safeTransferFrom", + "nodeType": "MemberAccess", + "referencedDeclaration": 32744, + "src": "1865:22:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_contract$_ERC20_$31408_$_t_address_$_t_address_$_t_uint256_$returns$__$bound_to$_t_contract$_ERC20_$31408_$", + "typeString": "function (contract ERC20,address,address,uint256)" + } + }, + "id": 2528, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "1865:57:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2529, + "nodeType": "ExpressionStatement", + "src": "1865:57:4" + }, + { + "expression": { + "arguments": [ + { + "id": 2531, + "name": "receiver", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2501, + "src": "1939:8:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2532, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2504, + "src": "1949:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2530, + "name": "_mint", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 31379, + "src": "1933:5:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_address_$_t_uint256_$returns$__$", + "typeString": "function (address,uint256)" + } + }, + "id": 2533, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "1933:23:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2534, + "nodeType": "ExpressionStatement", + "src": "1933:23:4" + }, + { + "eventCall": { + "arguments": [ + { + "expression": { + "id": 2536, + "name": "msg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -15, + "src": "1980:3:4", + "typeDescriptions": { + "typeIdentifier": "t_magic_message", + "typeString": "msg" + } + }, + "id": 2537, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "sender", + "nodeType": "MemberAccess", + "src": "1980:10:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2538, + "name": "receiver", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2501, + "src": "1992:8:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2539, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2499, + "src": "2002:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + { + "id": 2540, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2504, + "src": "2010:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2535, + "name": "Deposit", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2460, + "src": "1972:7:4", + "typeDescriptions": { + "typeIdentifier": "t_function_event_nonpayable$_t_address_$_t_address_$_t_uint256_$_t_uint256_$returns$__$", + "typeString": "function (address,address,uint256,uint256)" + } + }, + "id": 2541, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "1972:45:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2542, + "nodeType": "EmitStatement", + "src": "1967:50:4" + }, + { + "expression": { + "arguments": [ + { + "id": 2544, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2499, + "src": "2041:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + { + "id": 2545, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2504, + "src": "2049:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2543, + "name": "afterDeposit", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2952, + "src": "2028:12:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_uint256_$_t_uint256_$returns$__$", + "typeString": "function (uint256,uint256)" + } + }, + "id": 2546, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "2028:28:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2547, + "nodeType": "ExpressionStatement", + "src": "2028:28:4" + } + ] + }, + "functionSelector": "6e553f65", + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "deposit", + "nameLocation": "1556:7:4", + "parameters": { + "id": 2502, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2499, + "mutability": "mutable", + "name": "assets", + "nameLocation": "1572:6:4", + "nodeType": "VariableDeclaration", + "scope": 2549, + "src": "1564:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2498, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "1564:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 2501, + "mutability": "mutable", + "name": "receiver", + "nameLocation": "1588:8:4", + "nodeType": "VariableDeclaration", + "scope": 2549, + "src": "1580:16:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 2500, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "1580:7:4", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + } + ], + "src": "1563:34:4" + }, + "returnParameters": { + "id": 2505, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2504, + "mutability": "mutable", + "name": "shares", + "nameLocation": "1630:6:4", + "nodeType": "VariableDeclaration", + "scope": 2549, + "src": "1622:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2503, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "1622:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "1621:16:4" + }, + "scope": 2953, + "stateMutability": "nonpayable", + "virtual": true, + "visibility": "public" + }, + { + "id": 2595, + "nodeType": "FunctionDefinition", + "src": "2069:467:4", + "body": { + "id": 2594, + "nodeType": "Block", + "src": "2157:379:4", + "statements": [ + { + "expression": { + "id": 2562, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "id": 2558, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2556, + "src": "2167:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "arguments": [ + { + "id": 2560, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2551, + "src": "2188:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2559, + "name": "previewMint", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2846, + "src": "2176:11:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$_t_uint256_$returns$_t_uint256_$", + "typeString": "function (uint256) view returns (uint256)" + } + }, + "id": 2561, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "2176:19:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "2167:28:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 2563, + "nodeType": "ExpressionStatement", + "src": "2167:28:4" + }, + { + "expression": { + "arguments": [ + { + "expression": { + "id": 2567, + "name": "msg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -15, + "src": "2361:3:4", + "typeDescriptions": { + "typeIdentifier": "t_magic_message", + "typeString": "msg" + } + }, + "id": 2568, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "sender", + "nodeType": "MemberAccess", + "src": "2361:10:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "arguments": [ + { + "id": 2571, + "name": "this", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -28, + "src": "2381:4:4", + "typeDescriptions": { + "typeIdentifier": "t_contract$_ERC4626_$2953", + "typeString": "contract ERC4626" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_contract$_ERC4626_$2953", + "typeString": "contract ERC4626" + } + ], + "id": 2570, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "nodeType": "ElementaryTypeNameExpression", + "src": "2373:7:4", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_address_$", + "typeString": "type(address)" + }, + "typeName": { + "id": 2569, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "2373:7:4", + "typeDescriptions": {} + } + }, + "id": 2572, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "typeConversion", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "2373:13:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2573, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2556, + "src": "2388:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "expression": { + "id": 2564, + "name": "asset", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2475, + "src": "2338:5:4", + "typeDescriptions": { + "typeIdentifier": "t_contract$_ERC20_$31408", + "typeString": "contract ERC20" + } + }, + "id": 2566, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "safeTransferFrom", + "nodeType": "MemberAccess", + "referencedDeclaration": 32744, + "src": "2338:22:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_contract$_ERC20_$31408_$_t_address_$_t_address_$_t_uint256_$returns$__$bound_to$_t_contract$_ERC20_$31408_$", + "typeString": "function (contract ERC20,address,address,uint256)" + } + }, + "id": 2574, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "2338:57:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2575, + "nodeType": "ExpressionStatement", + "src": "2338:57:4" + }, + { + "expression": { + "arguments": [ + { + "id": 2577, + "name": "receiver", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2553, + "src": "2412:8:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2578, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2551, + "src": "2422:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2576, + "name": "_mint", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 31379, + "src": "2406:5:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_address_$_t_uint256_$returns$__$", + "typeString": "function (address,uint256)" + } + }, + "id": 2579, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "2406:23:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2580, + "nodeType": "ExpressionStatement", + "src": "2406:23:4" + }, + { + "eventCall": { + "arguments": [ + { + "expression": { + "id": 2582, + "name": "msg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -15, + "src": "2453:3:4", + "typeDescriptions": { + "typeIdentifier": "t_magic_message", + "typeString": "msg" + } + }, + "id": 2583, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "sender", + "nodeType": "MemberAccess", + "src": "2453:10:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2584, + "name": "receiver", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2553, + "src": "2465:8:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2585, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2556, + "src": "2475:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + { + "id": 2586, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2551, + "src": "2483:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2581, + "name": "Deposit", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2460, + "src": "2445:7:4", + "typeDescriptions": { + "typeIdentifier": "t_function_event_nonpayable$_t_address_$_t_address_$_t_uint256_$_t_uint256_$returns$__$", + "typeString": "function (address,address,uint256,uint256)" + } + }, + "id": 2587, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "2445:45:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2588, + "nodeType": "EmitStatement", + "src": "2440:50:4" + }, + { + "expression": { + "arguments": [ + { + "id": 2590, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2556, + "src": "2514:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + { + "id": 2591, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2551, + "src": "2522:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2589, + "name": "afterDeposit", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2952, + "src": "2501:12:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_uint256_$_t_uint256_$returns$__$", + "typeString": "function (uint256,uint256)" + } + }, + "id": 2592, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "2501:28:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2593, + "nodeType": "ExpressionStatement", + "src": "2501:28:4" + } + ] + }, + "functionSelector": "94bf804d", + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "mint", + "nameLocation": "2078:4:4", + "parameters": { + "id": 2554, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2551, + "mutability": "mutable", + "name": "shares", + "nameLocation": "2091:6:4", + "nodeType": "VariableDeclaration", + "scope": 2595, + "src": "2083:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2550, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "2083:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 2553, + "mutability": "mutable", + "name": "receiver", + "nameLocation": "2107:8:4", + "nodeType": "VariableDeclaration", + "scope": 2595, + "src": "2099:16:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 2552, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "2099:7:4", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + } + ], + "src": "2082:34:4" + }, + "returnParameters": { + "id": 2557, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2556, + "mutability": "mutable", + "name": "assets", + "nameLocation": "2149:6:4", + "nodeType": "VariableDeclaration", + "scope": 2595, + "src": "2141:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2555, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "2141:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "2140:16:4" + }, + "scope": 2953, + "stateMutability": "nonpayable", + "virtual": true, + "visibility": "public" + }, + { + "id": 2673, + "nodeType": "FunctionDefinition", + "src": "2542:679:4", + "body": { + "id": 2672, + "nodeType": "Block", + "src": "2679:542:4", + "statements": [ + { + "expression": { + "id": 2610, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "id": 2606, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2604, + "src": "2689:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "arguments": [ + { + "id": 2608, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2597, + "src": "2714:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2607, + "name": "previewWithdraw", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2870, + "src": "2698:15:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$_t_uint256_$returns$_t_uint256_$", + "typeString": "function (uint256) view returns (uint256)" + } + }, + "id": 2609, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "2698:23:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "2689:32:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 2611, + "nodeType": "ExpressionStatement", + "src": "2689:32:4" + }, + { + "condition": { + "commonType": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "id": 2615, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "expression": { + "id": 2612, + "name": "msg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -15, + "src": "2803:3:4", + "typeDescriptions": { + "typeIdentifier": "t_magic_message", + "typeString": "msg" + } + }, + "id": 2613, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "sender", + "nodeType": "MemberAccess", + "src": "2803:10:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "nodeType": "BinaryOperation", + "operator": "!=", + "rightExpression": { + "id": 2614, + "name": "owner", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2601, + "src": "2817:5:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "src": "2803:19:4", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "id": 2645, + "nodeType": "IfStatement", + "src": "2799:228:4", + "trueBody": { + "id": 2644, + "nodeType": "Block", + "src": "2824:203:4", + "statements": [ + { + "assignments": [ + 2617 + ], + "declarations": [ + { + "constant": false, + "id": 2617, + "mutability": "mutable", + "name": "allowed", + "nameLocation": "2846:7:4", + "nodeType": "VariableDeclaration", + "scope": 2644, + "src": "2838:15:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2616, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "2838:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "id": 2624, + "initialValue": { + "baseExpression": { + "baseExpression": { + "id": 2618, + "name": "allowance", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 31057, + "src": "2856:9:4", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_address_$_t_mapping$_t_address_$_t_uint256_$_$", + "typeString": "mapping(address => mapping(address => uint256))" + } + }, + "id": 2620, + "indexExpression": { + "id": 2619, + "name": "owner", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2601, + "src": "2866:5:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "2856:16:4", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_address_$_t_uint256_$", + "typeString": "mapping(address => uint256)" + } + }, + "id": 2623, + "indexExpression": { + "expression": { + "id": 2621, + "name": "msg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -15, + "src": "2873:3:4", + "typeDescriptions": { + "typeIdentifier": "t_magic_message", + "typeString": "msg" + } + }, + "id": 2622, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "sender", + "nodeType": "MemberAccess", + "src": "2873:10:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "2856:28:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "VariableDeclarationStatement", + "src": "2838:46:4" + }, + { + "condition": { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 2631, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "id": 2625, + "name": "allowed", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2617, + "src": "2939:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": "!=", + "rightExpression": { + "expression": { + "arguments": [ + { + "id": 2628, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "nodeType": "ElementaryTypeNameExpression", + "src": "2955:7:4", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_uint256_$", + "typeString": "type(uint256)" + }, + "typeName": { + "id": 2627, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "2955:7:4", + "typeDescriptions": {} + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_type$_t_uint256_$", + "typeString": "type(uint256)" + } + ], + "id": 2626, + "name": "type", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -27, + "src": "2950:4:4", + "typeDescriptions": { + "typeIdentifier": "t_function_metatype_pure$__$returns$__$", + "typeString": "function () pure" + } + }, + "id": 2629, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "2950:13:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_magic_meta_type_t_uint256", + "typeString": "type(uint256)" + } + }, + "id": 2630, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "memberName": "max", + "nodeType": "MemberAccess", + "src": "2950:17:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "2939:28:4", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "id": 2643, + "nodeType": "IfStatement", + "src": "2935:81:4", + "trueBody": { + "expression": { + "id": 2641, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "baseExpression": { + "baseExpression": { + "id": 2632, + "name": "allowance", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 31057, + "src": "2969:9:4", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_address_$_t_mapping$_t_address_$_t_uint256_$_$", + "typeString": "mapping(address => mapping(address => uint256))" + } + }, + "id": 2636, + "indexExpression": { + "id": 2633, + "name": "owner", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2601, + "src": "2979:5:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "2969:16:4", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_address_$_t_uint256_$", + "typeString": "mapping(address => uint256)" + } + }, + "id": 2637, + "indexExpression": { + "expression": { + "id": 2634, + "name": "msg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -15, + "src": "2986:3:4", + "typeDescriptions": { + "typeIdentifier": "t_magic_message", + "typeString": "msg" + } + }, + "id": 2635, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "sender", + "nodeType": "MemberAccess", + "src": "2986:10:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": true, + "nodeType": "IndexAccess", + "src": "2969:28:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 2640, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "id": 2638, + "name": "allowed", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2617, + "src": "3000:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": "-", + "rightExpression": { + "id": 2639, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2604, + "src": "3010:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "3000:16:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "2969:47:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 2642, + "nodeType": "ExpressionStatement", + "src": "2969:47:4" + } + } + ] + } + }, + { + "expression": { + "arguments": [ + { + "id": 2647, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2597, + "src": "3052:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + { + "id": 2648, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2604, + "src": "3060:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2646, + "name": "beforeWithdraw", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2944, + "src": "3037:14:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_uint256_$_t_uint256_$returns$__$", + "typeString": "function (uint256,uint256)" + } + }, + "id": 2649, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "3037:30:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2650, + "nodeType": "ExpressionStatement", + "src": "3037:30:4" + }, + { + "expression": { + "arguments": [ + { + "id": 2652, + "name": "owner", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2601, + "src": "3084:5:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2653, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2604, + "src": "3091:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2651, + "name": "_burn", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 31407, + "src": "3078:5:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_address_$_t_uint256_$returns$__$", + "typeString": "function (address,uint256)" + } + }, + "id": 2654, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "3078:20:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2655, + "nodeType": "ExpressionStatement", + "src": "3078:20:4" + }, + { + "eventCall": { + "arguments": [ + { + "expression": { + "id": 2657, + "name": "msg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -15, + "src": "3123:3:4", + "typeDescriptions": { + "typeIdentifier": "t_magic_message", + "typeString": "msg" + } + }, + "id": 2658, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "sender", + "nodeType": "MemberAccess", + "src": "3123:10:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2659, + "name": "receiver", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2599, + "src": "3135:8:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2660, + "name": "owner", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2601, + "src": "3145:5:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2661, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2597, + "src": "3152:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + { + "id": 2662, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2604, + "src": "3160:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2656, + "name": "Withdraw", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2472, + "src": "3114:8:4", + "typeDescriptions": { + "typeIdentifier": "t_function_event_nonpayable$_t_address_$_t_address_$_t_address_$_t_uint256_$_t_uint256_$returns$__$", + "typeString": "function (address,address,address,uint256,uint256)" + } + }, + "id": 2663, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "3114:53:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2664, + "nodeType": "EmitStatement", + "src": "3109:58:4" + }, + { + "expression": { + "arguments": [ + { + "id": 2668, + "name": "receiver", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2599, + "src": "3197:8:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2669, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2597, + "src": "3207:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "expression": { + "id": 2665, + "name": "asset", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2475, + "src": "3178:5:4", + "typeDescriptions": { + "typeIdentifier": "t_contract$_ERC20_$31408", + "typeString": "contract ERC20" + } + }, + "id": 2667, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "safeTransfer", + "nodeType": "MemberAccess", + "referencedDeclaration": 32764, + "src": "3178:18:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_contract$_ERC20_$31408_$_t_address_$_t_uint256_$returns$__$bound_to$_t_contract$_ERC20_$31408_$", + "typeString": "function (contract ERC20,address,uint256)" + } + }, + "id": 2670, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "3178:36:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2671, + "nodeType": "ExpressionStatement", + "src": "3178:36:4" + } + ] + }, + "functionSelector": "b460af94", + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "withdraw", + "nameLocation": "2551:8:4", + "parameters": { + "id": 2602, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2597, + "mutability": "mutable", + "name": "assets", + "nameLocation": "2577:6:4", + "nodeType": "VariableDeclaration", + "scope": 2673, + "src": "2569:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2596, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "2569:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 2599, + "mutability": "mutable", + "name": "receiver", + "nameLocation": "2601:8:4", + "nodeType": "VariableDeclaration", + "scope": 2673, + "src": "2593:16:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 2598, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "2593:7:4", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 2601, + "mutability": "mutable", + "name": "owner", + "nameLocation": "2627:5:4", + "nodeType": "VariableDeclaration", + "scope": 2673, + "src": "2619:13:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 2600, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "2619:7:4", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + } + ], + "src": "2559:79:4" + }, + "returnParameters": { + "id": 2605, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2604, + "mutability": "mutable", + "name": "shares", + "nameLocation": "2671:6:4", + "nodeType": "VariableDeclaration", + "scope": 2673, + "src": "2663:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2603, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "2663:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "2662:16:4" + }, + "scope": 2953, + "stateMutability": "nonpayable", + "virtual": true, + "visibility": "public" + }, + { + "id": 2757, + "nodeType": "FunctionDefinition", + "src": "3227:713:4", + "body": { + "id": 2756, + "nodeType": "Block", + "src": "3362:578:4", + "statements": [ + { + "condition": { + "commonType": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "id": 2687, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "expression": { + "id": 2684, + "name": "msg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -15, + "src": "3376:3:4", + "typeDescriptions": { + "typeIdentifier": "t_magic_message", + "typeString": "msg" + } + }, + "id": 2685, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "sender", + "nodeType": "MemberAccess", + "src": "3376:10:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "nodeType": "BinaryOperation", + "operator": "!=", + "rightExpression": { + "id": 2686, + "name": "owner", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2679, + "src": "3390:5:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "src": "3376:19:4", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "id": 2717, + "nodeType": "IfStatement", + "src": "3372:228:4", + "trueBody": { + "id": 2716, + "nodeType": "Block", + "src": "3397:203:4", + "statements": [ + { + "assignments": [ + 2689 + ], + "declarations": [ + { + "constant": false, + "id": 2689, + "mutability": "mutable", + "name": "allowed", + "nameLocation": "3419:7:4", + "nodeType": "VariableDeclaration", + "scope": 2716, + "src": "3411:15:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2688, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "3411:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "id": 2696, + "initialValue": { + "baseExpression": { + "baseExpression": { + "id": 2690, + "name": "allowance", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 31057, + "src": "3429:9:4", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_address_$_t_mapping$_t_address_$_t_uint256_$_$", + "typeString": "mapping(address => mapping(address => uint256))" + } + }, + "id": 2692, + "indexExpression": { + "id": 2691, + "name": "owner", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2679, + "src": "3439:5:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "3429:16:4", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_address_$_t_uint256_$", + "typeString": "mapping(address => uint256)" + } + }, + "id": 2695, + "indexExpression": { + "expression": { + "id": 2693, + "name": "msg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -15, + "src": "3446:3:4", + "typeDescriptions": { + "typeIdentifier": "t_magic_message", + "typeString": "msg" + } + }, + "id": 2694, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "sender", + "nodeType": "MemberAccess", + "src": "3446:10:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "3429:28:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "VariableDeclarationStatement", + "src": "3411:46:4" + }, + { + "condition": { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 2703, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "id": 2697, + "name": "allowed", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2689, + "src": "3512:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": "!=", + "rightExpression": { + "expression": { + "arguments": [ + { + "id": 2700, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "nodeType": "ElementaryTypeNameExpression", + "src": "3528:7:4", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_uint256_$", + "typeString": "type(uint256)" + }, + "typeName": { + "id": 2699, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "3528:7:4", + "typeDescriptions": {} + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_type$_t_uint256_$", + "typeString": "type(uint256)" + } + ], + "id": 2698, + "name": "type", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -27, + "src": "3523:4:4", + "typeDescriptions": { + "typeIdentifier": "t_function_metatype_pure$__$returns$__$", + "typeString": "function () pure" + } + }, + "id": 2701, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "3523:13:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_magic_meta_type_t_uint256", + "typeString": "type(uint256)" + } + }, + "id": 2702, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "memberName": "max", + "nodeType": "MemberAccess", + "src": "3523:17:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "3512:28:4", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "id": 2715, + "nodeType": "IfStatement", + "src": "3508:81:4", + "trueBody": { + "expression": { + "id": 2713, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "baseExpression": { + "baseExpression": { + "id": 2704, + "name": "allowance", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 31057, + "src": "3542:9:4", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_address_$_t_mapping$_t_address_$_t_uint256_$_$", + "typeString": "mapping(address => mapping(address => uint256))" + } + }, + "id": 2708, + "indexExpression": { + "id": 2705, + "name": "owner", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2679, + "src": "3552:5:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "3542:16:4", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_address_$_t_uint256_$", + "typeString": "mapping(address => uint256)" + } + }, + "id": 2709, + "indexExpression": { + "expression": { + "id": 2706, + "name": "msg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -15, + "src": "3559:3:4", + "typeDescriptions": { + "typeIdentifier": "t_magic_message", + "typeString": "msg" + } + }, + "id": 2707, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "sender", + "nodeType": "MemberAccess", + "src": "3559:10:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": true, + "nodeType": "IndexAccess", + "src": "3542:28:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 2712, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "id": 2710, + "name": "allowed", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2689, + "src": "3573:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": "-", + "rightExpression": { + "id": 2711, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2675, + "src": "3583:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "3573:16:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "3542:47:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 2714, + "nodeType": "ExpressionStatement", + "src": "3542:47:4" + } + } + ] + } + }, + { + "expression": { + "arguments": [ + { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 2726, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "components": [ + { + "id": 2723, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "id": 2719, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2682, + "src": "3693:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "arguments": [ + { + "id": 2721, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2675, + "src": "3716:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2720, + "name": "previewRedeem", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2882, + "src": "3702:13:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$_t_uint256_$returns$_t_uint256_$", + "typeString": "function (uint256) view returns (uint256)" + } + }, + "id": 2722, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "3702:21:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "3693:30:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "id": 2724, + "isConstant": false, + "isInlineArray": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "TupleExpression", + "src": "3692:32:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": "!=", + "rightExpression": { + "hexValue": "30", + "id": 2725, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "3728:1:4", + "typeDescriptions": { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + }, + "value": "0" + }, + "src": "3692:37:4", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + { + "hexValue": "5a45524f5f415353455453", + "id": 2727, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "3731:13:4", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_bf169ab2ef12d57708bb5afe72ea54ba3ad2eccb91dd95f37571afa377c52483", + "typeString": "literal_string \"ZERO_ASSETS\"" + }, + "value": "ZERO_ASSETS" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + { + "typeIdentifier": "t_stringliteral_bf169ab2ef12d57708bb5afe72ea54ba3ad2eccb91dd95f37571afa377c52483", + "typeString": "literal_string \"ZERO_ASSETS\"" + } + ], + "id": 2718, + "name": "require", + "nodeType": "Identifier", + "overloadedDeclarations": [ + -18, + -18 + ], + "referencedDeclaration": -18, + "src": "3684:7:4", + "typeDescriptions": { + "typeIdentifier": "t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$", + "typeString": "function (bool,string memory) pure" + } + }, + "id": 2728, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "3684:61:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2729, + "nodeType": "ExpressionStatement", + "src": "3684:61:4" + }, + { + "expression": { + "arguments": [ + { + "id": 2731, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2682, + "src": "3771:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + { + "id": 2732, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2675, + "src": "3779:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2730, + "name": "beforeWithdraw", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2944, + "src": "3756:14:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_uint256_$_t_uint256_$returns$__$", + "typeString": "function (uint256,uint256)" + } + }, + "id": 2733, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "3756:30:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2734, + "nodeType": "ExpressionStatement", + "src": "3756:30:4" + }, + { + "expression": { + "arguments": [ + { + "id": 2736, + "name": "owner", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2679, + "src": "3803:5:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2737, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2675, + "src": "3810:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2735, + "name": "_burn", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 31407, + "src": "3797:5:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_address_$_t_uint256_$returns$__$", + "typeString": "function (address,uint256)" + } + }, + "id": 2738, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "3797:20:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2739, + "nodeType": "ExpressionStatement", + "src": "3797:20:4" + }, + { + "eventCall": { + "arguments": [ + { + "expression": { + "id": 2741, + "name": "msg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -15, + "src": "3842:3:4", + "typeDescriptions": { + "typeIdentifier": "t_magic_message", + "typeString": "msg" + } + }, + "id": 2742, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "sender", + "nodeType": "MemberAccess", + "src": "3842:10:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2743, + "name": "receiver", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2677, + "src": "3854:8:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2744, + "name": "owner", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2679, + "src": "3864:5:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2745, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2682, + "src": "3871:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + { + "id": 2746, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2675, + "src": "3879:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2740, + "name": "Withdraw", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2472, + "src": "3833:8:4", + "typeDescriptions": { + "typeIdentifier": "t_function_event_nonpayable$_t_address_$_t_address_$_t_address_$_t_uint256_$_t_uint256_$returns$__$", + "typeString": "function (address,address,address,uint256,uint256)" + } + }, + "id": 2747, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "3833:53:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2748, + "nodeType": "EmitStatement", + "src": "3828:58:4" + }, + { + "expression": { + "arguments": [ + { + "id": 2752, + "name": "receiver", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2677, + "src": "3916:8:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 2753, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2682, + "src": "3926:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "expression": { + "id": 2749, + "name": "asset", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2475, + "src": "3897:5:4", + "typeDescriptions": { + "typeIdentifier": "t_contract$_ERC20_$31408", + "typeString": "contract ERC20" + } + }, + "id": 2751, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "safeTransfer", + "nodeType": "MemberAccess", + "referencedDeclaration": 32764, + "src": "3897:18:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_contract$_ERC20_$31408_$_t_address_$_t_uint256_$returns$__$bound_to$_t_contract$_ERC20_$31408_$", + "typeString": "function (contract ERC20,address,uint256)" + } + }, + "id": 2754, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "3897:36:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 2755, + "nodeType": "ExpressionStatement", + "src": "3897:36:4" + } + ] + }, + "functionSelector": "ba087652", + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "redeem", + "nameLocation": "3236:6:4", + "parameters": { + "id": 2680, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2675, + "mutability": "mutable", + "name": "shares", + "nameLocation": "3260:6:4", + "nodeType": "VariableDeclaration", + "scope": 2757, + "src": "3252:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2674, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "3252:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 2677, + "mutability": "mutable", + "name": "receiver", + "nameLocation": "3284:8:4", + "nodeType": "VariableDeclaration", + "scope": 2757, + "src": "3276:16:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 2676, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "3276:7:4", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 2679, + "mutability": "mutable", + "name": "owner", + "nameLocation": "3310:5:4", + "nodeType": "VariableDeclaration", + "scope": 2757, + "src": "3302:13:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 2678, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "3302:7:4", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + } + ], + "src": "3242:79:4" + }, + "returnParameters": { + "id": 2683, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2682, + "mutability": "mutable", + "name": "assets", + "nameLocation": "3354:6:4", + "nodeType": "VariableDeclaration", + "scope": 2757, + "src": "3346:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2681, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "3346:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "3345:16:4" + }, + "scope": 2953, + "stateMutability": "nonpayable", + "virtual": true, + "visibility": "public" + }, + { + "id": 2762, + "nodeType": "FunctionDefinition", + "src": "4130:61:4", + "functionSelector": "01e1d114", + "implemented": false, + "kind": "function", + "modifiers": [], + "name": "totalAssets", + "nameLocation": "4139:11:4", + "parameters": { + "id": 2758, + "nodeType": "ParameterList", + "parameters": [], + "src": "4150:2:4" + }, + "returnParameters": { + "id": 2761, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2760, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 2762, + "src": "4182:7:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2759, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "4182:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "4181:9:4" + }, + "scope": 2953, + "stateMutability": "view", + "virtual": true, + "visibility": "public" + }, + { + "id": 2786, + "nodeType": "FunctionDefinition", + "src": "4197:257:4", + "body": { + "id": 2785, + "nodeType": "Block", + "src": "4276:178:4", + "statements": [ + { + "assignments": [ + 2770 + ], + "declarations": [ + { + "constant": false, + "id": 2770, + "mutability": "mutable", + "name": "supply", + "nameLocation": "4294:6:4", + "nodeType": "VariableDeclaration", + "scope": 2785, + "src": "4286:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2769, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "4286:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "id": 2772, + "initialValue": { + "id": 2771, + "name": "totalSupply", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 31047, + "src": "4303:11:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "VariableDeclarationStatement", + "src": "4286:28:4" + }, + { + "expression": { + "condition": { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 2775, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "id": 2773, + "name": "supply", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2770, + "src": "4384:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": "==", + "rightExpression": { + "hexValue": "30", + "id": 2774, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "4394:1:4", + "typeDescriptions": { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + }, + "value": "0" + }, + "src": "4384:11:4", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "falseExpression": { + "arguments": [ + { + "id": 2779, + "name": "supply", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2770, + "src": "4425:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + { + "arguments": [], + "expression": { + "argumentTypes": [], + "id": 2780, + "name": "totalAssets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2762, + "src": "4433:11:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$__$returns$_t_uint256_$", + "typeString": "function () view returns (uint256)" + } + }, + "id": 2781, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "4433:13:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "expression": { + "id": 2777, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2764, + "src": "4407:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 2778, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "mulDivDown", + "nodeType": "MemberAccess", + "referencedDeclaration": 32285, + "src": "4407:17:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_pure$_t_uint256_$_t_uint256_$_t_uint256_$returns$_t_uint256_$bound_to$_t_uint256_$", + "typeString": "function (uint256,uint256,uint256) pure returns (uint256)" + } + }, + "id": 2782, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "4407:40:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 2783, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "Conditional", + "src": "4384:63:4", + "trueExpression": { + "id": 2776, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2764, + "src": "4398:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "functionReturnParameters": 2768, + "id": 2784, + "nodeType": "Return", + "src": "4377:70:4" + } + ] + }, + "functionSelector": "c6e6f592", + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "convertToShares", + "nameLocation": "4206:15:4", + "parameters": { + "id": 2765, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2764, + "mutability": "mutable", + "name": "assets", + "nameLocation": "4230:6:4", + "nodeType": "VariableDeclaration", + "scope": 2786, + "src": "4222:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2763, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "4222:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "4221:16:4" + }, + "returnParameters": { + "id": 2768, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2767, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 2786, + "src": "4267:7:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2766, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "4267:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "4266:9:4" + }, + "scope": 2953, + "stateMutability": "view", + "virtual": true, + "visibility": "public" + }, + { + "id": 2810, + "nodeType": "FunctionDefinition", + "src": "4460:257:4", + "body": { + "id": 2809, + "nodeType": "Block", + "src": "4539:178:4", + "statements": [ + { + "assignments": [ + 2794 + ], + "declarations": [ + { + "constant": false, + "id": 2794, + "mutability": "mutable", + "name": "supply", + "nameLocation": "4557:6:4", + "nodeType": "VariableDeclaration", + "scope": 2809, + "src": "4549:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2793, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "4549:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "id": 2796, + "initialValue": { + "id": 2795, + "name": "totalSupply", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 31047, + "src": "4566:11:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "VariableDeclarationStatement", + "src": "4549:28:4" + }, + { + "expression": { + "condition": { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 2799, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "id": 2797, + "name": "supply", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2794, + "src": "4647:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": "==", + "rightExpression": { + "hexValue": "30", + "id": 2798, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "4657:1:4", + "typeDescriptions": { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + }, + "value": "0" + }, + "src": "4647:11:4", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "falseExpression": { + "arguments": [ + { + "arguments": [], + "expression": { + "argumentTypes": [], + "id": 2803, + "name": "totalAssets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2762, + "src": "4688:11:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$__$returns$_t_uint256_$", + "typeString": "function () view returns (uint256)" + } + }, + "id": 2804, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "4688:13:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + { + "id": 2805, + "name": "supply", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2794, + "src": "4703:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "expression": { + "id": 2801, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2788, + "src": "4670:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 2802, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "mulDivDown", + "nodeType": "MemberAccess", + "referencedDeclaration": 32285, + "src": "4670:17:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_pure$_t_uint256_$_t_uint256_$_t_uint256_$returns$_t_uint256_$bound_to$_t_uint256_$", + "typeString": "function (uint256,uint256,uint256) pure returns (uint256)" + } + }, + "id": 2806, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "4670:40:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 2807, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "Conditional", + "src": "4647:63:4", + "trueExpression": { + "id": 2800, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2788, + "src": "4661:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "functionReturnParameters": 2792, + "id": 2808, + "nodeType": "Return", + "src": "4640:70:4" + } + ] + }, + "functionSelector": "07a2d13a", + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "convertToAssets", + "nameLocation": "4469:15:4", + "parameters": { + "id": 2789, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2788, + "mutability": "mutable", + "name": "shares", + "nameLocation": "4493:6:4", + "nodeType": "VariableDeclaration", + "scope": 2810, + "src": "4485:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2787, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "4485:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "4484:16:4" + }, + "returnParameters": { + "id": 2792, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2791, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 2810, + "src": "4530:7:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2790, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "4530:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "4529:9:4" + }, + "scope": 2953, + "stateMutability": "view", + "virtual": true, + "visibility": "public" + }, + { + "id": 2822, + "nodeType": "FunctionDefinition", + "src": "4723:125:4", + "body": { + "id": 2821, + "nodeType": "Block", + "src": "4801:47:4", + "statements": [ + { + "expression": { + "arguments": [ + { + "id": 2818, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2812, + "src": "4834:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2817, + "name": "convertToShares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2786, + "src": "4818:15:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$_t_uint256_$returns$_t_uint256_$", + "typeString": "function (uint256) view returns (uint256)" + } + }, + "id": 2819, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "4818:23:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "functionReturnParameters": 2816, + "id": 2820, + "nodeType": "Return", + "src": "4811:30:4" + } + ] + }, + "functionSelector": "ef8b30f7", + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "previewDeposit", + "nameLocation": "4732:14:4", + "parameters": { + "id": 2813, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2812, + "mutability": "mutable", + "name": "assets", + "nameLocation": "4755:6:4", + "nodeType": "VariableDeclaration", + "scope": 2822, + "src": "4747:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2811, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "4747:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "4746:16:4" + }, + "returnParameters": { + "id": 2816, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2815, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 2822, + "src": "4792:7:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2814, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "4792:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "4791:9:4" + }, + "scope": 2953, + "stateMutability": "view", + "virtual": true, + "visibility": "public" + }, + { + "id": 2846, + "nodeType": "FunctionDefinition", + "src": "4854:251:4", + "body": { + "id": 2845, + "nodeType": "Block", + "src": "4929:176:4", + "statements": [ + { + "assignments": [ + 2830 + ], + "declarations": [ + { + "constant": false, + "id": 2830, + "mutability": "mutable", + "name": "supply", + "nameLocation": "4947:6:4", + "nodeType": "VariableDeclaration", + "scope": 2845, + "src": "4939:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2829, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "4939:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "id": 2832, + "initialValue": { + "id": 2831, + "name": "totalSupply", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 31047, + "src": "4956:11:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "VariableDeclarationStatement", + "src": "4939:28:4" + }, + { + "expression": { + "condition": { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 2835, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "id": 2833, + "name": "supply", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2830, + "src": "5037:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": "==", + "rightExpression": { + "hexValue": "30", + "id": 2834, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "5047:1:4", + "typeDescriptions": { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + }, + "value": "0" + }, + "src": "5037:11:4", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "falseExpression": { + "arguments": [ + { + "arguments": [], + "expression": { + "argumentTypes": [], + "id": 2839, + "name": "totalAssets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2762, + "src": "5076:11:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$__$returns$_t_uint256_$", + "typeString": "function () view returns (uint256)" + } + }, + "id": 2840, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "5076:13:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + { + "id": 2841, + "name": "supply", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2830, + "src": "5091:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "expression": { + "id": 2837, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2824, + "src": "5060:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 2838, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "mulDivUp", + "nodeType": "MemberAccess", + "referencedDeclaration": 32298, + "src": "5060:15:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_pure$_t_uint256_$_t_uint256_$_t_uint256_$returns$_t_uint256_$bound_to$_t_uint256_$", + "typeString": "function (uint256,uint256,uint256) pure returns (uint256)" + } + }, + "id": 2842, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "5060:38:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 2843, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "Conditional", + "src": "5037:61:4", + "trueExpression": { + "id": 2836, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2824, + "src": "5051:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "functionReturnParameters": 2828, + "id": 2844, + "nodeType": "Return", + "src": "5030:68:4" + } + ] + }, + "functionSelector": "b3d7f6b9", + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "previewMint", + "nameLocation": "4863:11:4", + "parameters": { + "id": 2825, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2824, + "mutability": "mutable", + "name": "shares", + "nameLocation": "4883:6:4", + "nodeType": "VariableDeclaration", + "scope": 2846, + "src": "4875:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2823, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "4875:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "4874:16:4" + }, + "returnParameters": { + "id": 2828, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2827, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 2846, + "src": "4920:7:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2826, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "4920:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "4919:9:4" + }, + "scope": 2953, + "stateMutability": "view", + "virtual": true, + "visibility": "public" + }, + { + "id": 2870, + "nodeType": "FunctionDefinition", + "src": "5111:255:4", + "body": { + "id": 2869, + "nodeType": "Block", + "src": "5190:176:4", + "statements": [ + { + "assignments": [ + 2854 + ], + "declarations": [ + { + "constant": false, + "id": 2854, + "mutability": "mutable", + "name": "supply", + "nameLocation": "5208:6:4", + "nodeType": "VariableDeclaration", + "scope": 2869, + "src": "5200:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2853, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "5200:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "id": 2856, + "initialValue": { + "id": 2855, + "name": "totalSupply", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 31047, + "src": "5217:11:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "VariableDeclarationStatement", + "src": "5200:28:4" + }, + { + "expression": { + "condition": { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 2859, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "id": 2857, + "name": "supply", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2854, + "src": "5298:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": "==", + "rightExpression": { + "hexValue": "30", + "id": 2858, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "5308:1:4", + "typeDescriptions": { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + }, + "value": "0" + }, + "src": "5298:11:4", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "falseExpression": { + "arguments": [ + { + "id": 2863, + "name": "supply", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2854, + "src": "5337:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + { + "arguments": [], + "expression": { + "argumentTypes": [], + "id": 2864, + "name": "totalAssets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2762, + "src": "5345:11:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$__$returns$_t_uint256_$", + "typeString": "function () view returns (uint256)" + } + }, + "id": 2865, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "5345:13:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "expression": { + "id": 2861, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2848, + "src": "5321:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 2862, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberName": "mulDivUp", + "nodeType": "MemberAccess", + "referencedDeclaration": 32298, + "src": "5321:15:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_pure$_t_uint256_$_t_uint256_$_t_uint256_$returns$_t_uint256_$bound_to$_t_uint256_$", + "typeString": "function (uint256,uint256,uint256) pure returns (uint256)" + } + }, + "id": 2866, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "5321:38:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 2867, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "Conditional", + "src": "5298:61:4", + "trueExpression": { + "id": 2860, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2848, + "src": "5312:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "functionReturnParameters": 2852, + "id": 2868, + "nodeType": "Return", + "src": "5291:68:4" + } + ] + }, + "functionSelector": "0a28a477", + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "previewWithdraw", + "nameLocation": "5120:15:4", + "parameters": { + "id": 2849, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2848, + "mutability": "mutable", + "name": "assets", + "nameLocation": "5144:6:4", + "nodeType": "VariableDeclaration", + "scope": 2870, + "src": "5136:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2847, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "5136:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "5135:16:4" + }, + "returnParameters": { + "id": 2852, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2851, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 2870, + "src": "5181:7:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2850, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "5181:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "5180:9:4" + }, + "scope": 2953, + "stateMutability": "view", + "virtual": true, + "visibility": "public" + }, + { + "id": 2882, + "nodeType": "FunctionDefinition", + "src": "5372:124:4", + "body": { + "id": 2881, + "nodeType": "Block", + "src": "5449:47:4", + "statements": [ + { + "expression": { + "arguments": [ + { + "id": 2878, + "name": "shares", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2872, + "src": "5482:6:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2877, + "name": "convertToAssets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2810, + "src": "5466:15:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$_t_uint256_$returns$_t_uint256_$", + "typeString": "function (uint256) view returns (uint256)" + } + }, + "id": 2879, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "5466:23:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "functionReturnParameters": 2876, + "id": 2880, + "nodeType": "Return", + "src": "5459:30:4" + } + ] + }, + "functionSelector": "4cdad506", + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "previewRedeem", + "nameLocation": "5381:13:4", + "parameters": { + "id": 2873, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2872, + "mutability": "mutable", + "name": "shares", + "nameLocation": "5403:6:4", + "nodeType": "VariableDeclaration", + "scope": 2882, + "src": "5395:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2871, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "5395:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "5394:16:4" + }, + "returnParameters": { + "id": 2876, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2875, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 2882, + "src": "5440:7:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2874, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "5440:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "5439:9:4" + }, + "scope": 2953, + "stateMutability": "view", + "virtual": true, + "visibility": "public" + }, + { + "id": 2896, + "nodeType": "FunctionDefinition", + "src": "5693:108:4", + "body": { + "id": 2895, + "nodeType": "Block", + "src": "5760:41:4", + "statements": [ + { + "expression": { + "expression": { + "arguments": [ + { + "id": 2891, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "nodeType": "ElementaryTypeNameExpression", + "src": "5782:7:4", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_uint256_$", + "typeString": "type(uint256)" + }, + "typeName": { + "id": 2890, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "5782:7:4", + "typeDescriptions": {} + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_type$_t_uint256_$", + "typeString": "type(uint256)" + } + ], + "id": 2889, + "name": "type", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -27, + "src": "5777:4:4", + "typeDescriptions": { + "typeIdentifier": "t_function_metatype_pure$__$returns$__$", + "typeString": "function () pure" + } + }, + "id": 2892, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "5777:13:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_magic_meta_type_t_uint256", + "typeString": "type(uint256)" + } + }, + "id": 2893, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "memberName": "max", + "nodeType": "MemberAccess", + "src": "5777:17:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "functionReturnParameters": 2888, + "id": 2894, + "nodeType": "Return", + "src": "5770:24:4" + } + ] + }, + "functionSelector": "402d267d", + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "maxDeposit", + "nameLocation": "5702:10:4", + "parameters": { + "id": 2885, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2884, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 2896, + "src": "5713:7:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 2883, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "5713:7:4", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + } + ], + "src": "5712:9:4" + }, + "returnParameters": { + "id": 2888, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2887, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 2896, + "src": "5751:7:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2886, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "5751:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "5750:9:4" + }, + "scope": 2953, + "stateMutability": "view", + "virtual": true, + "visibility": "public" + }, + { + "id": 2910, + "nodeType": "FunctionDefinition", + "src": "5807:105:4", + "body": { + "id": 2909, + "nodeType": "Block", + "src": "5871:41:4", + "statements": [ + { + "expression": { + "expression": { + "arguments": [ + { + "id": 2905, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "nodeType": "ElementaryTypeNameExpression", + "src": "5893:7:4", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_uint256_$", + "typeString": "type(uint256)" + }, + "typeName": { + "id": 2904, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "5893:7:4", + "typeDescriptions": {} + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_type$_t_uint256_$", + "typeString": "type(uint256)" + } + ], + "id": 2903, + "name": "type", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": -27, + "src": "5888:4:4", + "typeDescriptions": { + "typeIdentifier": "t_function_metatype_pure$__$returns$__$", + "typeString": "function () pure" + } + }, + "id": 2906, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "5888:13:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_magic_meta_type_t_uint256", + "typeString": "type(uint256)" + } + }, + "id": 2907, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "memberName": "max", + "nodeType": "MemberAccess", + "src": "5888:17:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "functionReturnParameters": 2902, + "id": 2908, + "nodeType": "Return", + "src": "5881:24:4" + } + ] + }, + "functionSelector": "c63d75b6", + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "maxMint", + "nameLocation": "5816:7:4", + "parameters": { + "id": 2899, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2898, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 2910, + "src": "5824:7:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 2897, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "5824:7:4", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + } + ], + "src": "5823:9:4" + }, + "returnParameters": { + "id": 2902, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2901, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 2910, + "src": "5862:7:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2900, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "5862:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "5861:9:4" + }, + "scope": 2953, + "stateMutability": "view", + "virtual": true, + "visibility": "public" + }, + { + "id": 2924, + "nodeType": "FunctionDefinition", + "src": "5918:131:4", + "body": { + "id": 2923, + "nodeType": "Block", + "src": "5992:57:4", + "statements": [ + { + "expression": { + "arguments": [ + { + "baseExpression": { + "id": 2918, + "name": "balanceOf", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 31051, + "src": "6025:9:4", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_address_$_t_uint256_$", + "typeString": "mapping(address => uint256)" + } + }, + "id": 2920, + "indexExpression": { + "id": 2919, + "name": "owner", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2912, + "src": "6035:5:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "6025:16:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 2917, + "name": "convertToAssets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2810, + "src": "6009:15:4", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$_t_uint256_$returns$_t_uint256_$", + "typeString": "function (uint256) view returns (uint256)" + } + }, + "id": 2921, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "names": [], + "nodeType": "FunctionCall", + "src": "6009:33:4", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "functionReturnParameters": 2916, + "id": 2922, + "nodeType": "Return", + "src": "6002:40:4" + } + ] + }, + "functionSelector": "ce96cb77", + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "maxWithdraw", + "nameLocation": "5927:11:4", + "parameters": { + "id": 2913, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2912, + "mutability": "mutable", + "name": "owner", + "nameLocation": "5947:5:4", + "nodeType": "VariableDeclaration", + "scope": 2924, + "src": "5939:13:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 2911, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "5939:7:4", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + } + ], + "src": "5938:15:4" + }, + "returnParameters": { + "id": 2916, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2915, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 2924, + "src": "5983:7:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2914, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "5983:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "5982:9:4" + }, + "scope": 2953, + "stateMutability": "view", + "virtual": true, + "visibility": "public" + }, + { + "id": 2936, + "nodeType": "FunctionDefinition", + "src": "6055:112:4", + "body": { + "id": 2935, + "nodeType": "Block", + "src": "6127:40:4", + "statements": [ + { + "expression": { + "baseExpression": { + "id": 2931, + "name": "balanceOf", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 31051, + "src": "6144:9:4", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_address_$_t_uint256_$", + "typeString": "mapping(address => uint256)" + } + }, + "id": 2933, + "indexExpression": { + "id": 2932, + "name": "owner", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 2926, + "src": "6154:5:4", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "6144:16:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "functionReturnParameters": 2930, + "id": 2934, + "nodeType": "Return", + "src": "6137:23:4" + } + ] + }, + "functionSelector": "d905777e", + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "maxRedeem", + "nameLocation": "6064:9:4", + "parameters": { + "id": 2927, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2926, + "mutability": "mutable", + "name": "owner", + "nameLocation": "6082:5:4", + "nodeType": "VariableDeclaration", + "scope": 2936, + "src": "6074:13:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 2925, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "6074:7:4", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + } + ], + "src": "6073:15:4" + }, + "returnParameters": { + "id": 2930, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2929, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 2936, + "src": "6118:7:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2928, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "6118:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "6117:9:4" + }, + "scope": 2953, + "stateMutability": "view", + "virtual": true, + "visibility": "public" + }, + { + "id": 2944, + "nodeType": "FunctionDefinition", + "src": "6359:75:4", + "body": { + "id": 2943, + "nodeType": "Block", + "src": "6432:2:4", + "statements": [] + }, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "beforeWithdraw", + "nameLocation": "6368:14:4", + "parameters": { + "id": 2941, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2938, + "mutability": "mutable", + "name": "assets", + "nameLocation": "6391:6:4", + "nodeType": "VariableDeclaration", + "scope": 2944, + "src": "6383:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2937, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "6383:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 2940, + "mutability": "mutable", + "name": "shares", + "nameLocation": "6407:6:4", + "nodeType": "VariableDeclaration", + "scope": 2944, + "src": "6399:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2939, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "6399:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "6382:32:4" + }, + "returnParameters": { + "id": 2942, + "nodeType": "ParameterList", + "parameters": [], + "src": "6432:0:4" + }, + "scope": 2953, + "stateMutability": "nonpayable", + "virtual": true, + "visibility": "internal" + }, + { + "id": 2952, + "nodeType": "FunctionDefinition", + "src": "6440:73:4", + "body": { + "id": 2951, + "nodeType": "Block", + "src": "6511:2:4", + "statements": [] + }, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "afterDeposit", + "nameLocation": "6449:12:4", + "parameters": { + "id": 2949, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2946, + "mutability": "mutable", + "name": "assets", + "nameLocation": "6470:6:4", + "nodeType": "VariableDeclaration", + "scope": 2952, + "src": "6462:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2945, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "6462:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 2948, + "mutability": "mutable", + "name": "shares", + "nameLocation": "6486:6:4", + "nodeType": "VariableDeclaration", + "scope": 2952, + "src": "6478:14:4", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2947, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "6478:7:4", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "6461:32:4" + }, + "returnParameters": { + "id": 2950, + "nodeType": "ParameterList", + "parameters": [], + "src": "6511:0:4" + }, + "scope": 2953, + "stateMutability": "nonpayable", + "virtual": true, + "visibility": "internal" + } + ], + "abstract": true, + "baseContracts": [ + { + "baseName": { + "id": 2442, + "name": "ERC20", + "nodeType": "IdentifierPath", + "referencedDeclaration": 31408, + "src": "424:5:4" + }, + "id": 2443, + "nodeType": "InheritanceSpecifier", + "src": "424:5:4" + } + ], + "canonicalName": "ERC4626", + "contractDependencies": [], + "contractKind": "contract", + "documentation": { + "id": 2441, + "nodeType": "StructuredDocumentation", + "src": "240:155:4", + "text": "@notice Minimal ERC4626 tokenized Vault implementation.\n @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/mixins/ERC4626.sol)" + }, + "fullyImplemented": false, + "linearizedBaseContracts": [ + 2953, + 31408 + ], + "name": "ERC4626", + "nameLocation": "413:7:4", + "scope": 2954, + "usedErrors": [] + } + ], + "license": "AGPL-3.0-only" +} \ No newline at end of file diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000..87fbfc19 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,81 @@ +# Examples +- [ ] Address book +- [ ] Anvil + - [ ] Boot anvil + - [ ] Deploy contracts + - [x] Fork + - [ ] Testing +- [x] Big numbers + - [x] Comparison and equivalence + - [x] Conversion + - [x] Creating Instances + - [x] Math operations + - [x] Utilities +- [ ] Contracts + - [x] Abigen + - [x] Compile + - [ ] Creating Instances + - [x] Deploy Anvil + - [x] Deploy from ABI and bytecode + - [x] Deploy Moonbeam + - [x] Events + - [x] Events with meta + - [ ] Methods +- [ ] Events + - [ ] Logs and filtering + - [ ] Solidity topics +- [ ] Middleware + - [x] Builder + - [x] Create custom middleware + - [x] Gas escalator + - [x] Gas oracle + - [x] Nonce manager + - [x] Policy + - [x] Signer + - [ ] Time lag + - [ ] Transformer +- [ ] Providers + - [ ] Http + - [x] IPC + - [ ] Mock + - [x] Quorum + - [ ] Retry + - [x] RW + - [ ] WS +- [ ] Queries + - [ ] Blocks + - [x] Contracts + - [ ] Events + - [x] Paginated logs + - [x] UniswapV2 pair + - [ ] Transactions +- [x] Subscriptions + - [x] Watch blocks + - [x] Subscribe events by type + - [x] Subscribe logs +- [ ] Transactions + - [x] Call override + - [ ] Create raw transaction + - [ ] Create typed transaction + - [x] Decode input + - [ ] EIP-1559 + - [x] ENS + - [ ] Estimate gas + - [ ] Get gas price + - [x] Get gas price USD + - [x] Remove liquidity + - [ ] Set gas for a transaction + - [ ] Send raw transaction + - [ ] Send typed transaction + - [x] Trace + - [ ] Transaction receipt + - [ ] Transaction status + - [x] Transfer ETH +- [x] Wallets + - [x] Mnemonic + - [x] Ledger + - [x] Local + - [x] Permit hash + - [x] Sign message + - [x] Trezor + - [x] Yubi diff --git a/examples/anvil/Cargo.toml b/examples/anvil/Cargo.toml new file mode 100644 index 00000000..7e1fb044 --- /dev/null +++ b/examples/anvil/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "examples-anvil" +version = "1.0.2" +authors = ["Andrea Simeoni "] +edition = "2021" + +[dev-dependencies] +ethers = { path = "../..", version = "1.0.0" } +eyre = "0.6" +tokio = { version = "1.18", features = ["full"] } diff --git a/examples/anvil/README.md b/examples/anvil/README.md new file mode 100644 index 00000000..e69de29b diff --git a/examples/anvil_fork.rs b/examples/anvil/examples/anvil_fork.rs similarity index 88% rename from examples/anvil_fork.rs rename to examples/anvil/examples/anvil_fork.rs index 31da8670..4fecb8e8 100644 --- a/examples/anvil_fork.rs +++ b/examples/anvil/examples/anvil_fork.rs @@ -1,9 +1,10 @@ //! Spawn an [anvil](https://github.com/foundry-rs/foundry/tree/master/anvil) instance in forking mode use ethers::utils::Anvil; +use eyre::Result; #[tokio::main] -async fn main() -> eyre::Result<()> { +async fn main() -> Result<()> { // ensure `anvil` is available in $PATH let anvil = Anvil::new().fork("https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27").spawn(); diff --git a/examples/big-numbers/Cargo.toml b/examples/big-numbers/Cargo.toml new file mode 100644 index 00000000..04505c19 --- /dev/null +++ b/examples/big-numbers/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "examples-big-numbers" +version = "1.0.2" +authors = ["Andrea Simeoni "] +edition = "2021" + +[dev-dependencies] +#ethers-core = { version = "^1.0.0", path = "../../ethers-core" } +ethers = { path = "../..", version = "1.0.0" } + diff --git a/examples/big-numbers/README.md b/examples/big-numbers/README.md new file mode 100644 index 00000000..7ac9428e --- /dev/null +++ b/examples/big-numbers/README.md @@ -0,0 +1,7 @@ +# Big numbers +Ethereum uses big numbers (also known as "bignums" or "arbitrary-precision integers") to represent certain values in its codebase and in blockchain transactions. This is necessary because [the EVM](https://ethereum.org/en/developers/docs/evm) operates on a 256-bit word size, which is different from the usual 32-bit or 64-bit of modern machines. This was chosen for the ease of use with 256-bit cryptography (such as Keccak-256 hashes or secp256k1 signatures). + +It is worth noting that Ethereum is not the only blockchain or cryptocurrency that uses big numbers. Many other blockchains and cryptocurrencies also use big numbers to represent values in their respective systems. + +## Utilities +In order to create an application, it is often necessary to convert between the representation of values that is easily understood by humans (such as ether) and the machine-readable form that is used by contracts and math functions (such as wei). This is particularly important when working with Ethereum, as certain values, such as balances and gas prices, must be expressed in wei when sending transactions, even if they are displayed to the user in a different format, such as ether or gwei. To help with this conversion, ethers-rs provides two functions, `parse_units` and `format_units`, which allow you to easily convert between human-readable and machine-readable forms of values. `parse_units` can be used to convert a string representing a value in ether, such as "1.1", into a big number in wei, which can be used in contracts and math functions. `format_units` can be used to convert a big number value into a human-readable string, which is useful for displaying values to users. diff --git a/examples/big-numbers/examples/comparison_equivalence.rs b/examples/big-numbers/examples/comparison_equivalence.rs new file mode 100644 index 00000000..214a81e7 --- /dev/null +++ b/examples/big-numbers/examples/comparison_equivalence.rs @@ -0,0 +1,32 @@ +use ethers::types::U256; + +fn main() { + // a == b + let a = U256::from(100_u32); + let b = U256::from(100_u32); + assert!(a == b); + + // a < b + let a = U256::from(1_u32); + let b = U256::from(100_u32); + assert!(a < b); + + // a <= b + let a = U256::from(100_u32); + let b = U256::from(100_u32); + assert!(a <= b); + + // a > b + let a = U256::from(100_u32); + let b = U256::from(1_u32); + assert!(a > b); + + // a >= b + let a = U256::from(100_u32); + let b = U256::from(100_u32); + assert!(a >= b); + + // a == 0 + let a = U256::zero(); + assert!(a.is_zero()); +} diff --git a/examples/big-numbers/examples/conversion.rs b/examples/big-numbers/examples/conversion.rs new file mode 100644 index 00000000..99eb6ea0 --- /dev/null +++ b/examples/big-numbers/examples/conversion.rs @@ -0,0 +1,23 @@ +use ethers::{types::U256, utils::format_units}; + +fn main() { + let num = U256::from(42_u8); + + let a: u128 = num.as_u128(); + assert_eq!(a, 42); + + let b: u64 = num.as_u64(); + assert_eq!(b, 42); + + let c: u32 = num.as_u32(); + assert_eq!(c, 42); + + let d: usize = num.as_usize(); + assert_eq!(d, 42); + + let e: String = num.to_string(); + assert_eq!(e, "42"); + + let f: String = format_units(num, 4).unwrap(); + assert_eq!(f, "0.0042"); +} diff --git a/examples/big-numbers/examples/create_instances.rs b/examples/big-numbers/examples/create_instances.rs new file mode 100644 index 00000000..35fea7f1 --- /dev/null +++ b/examples/big-numbers/examples/create_instances.rs @@ -0,0 +1,47 @@ +use ethers::{ + types::{serde_helpers::Numeric, U256}, + utils::{parse_units, ParseUnits}, +}; + +fn main() { + // From strings + let a = U256::from_dec_str("42").unwrap(); + assert_eq!(format!("{a:?}"), "42"); + + let amount = "42"; + let units = 4; + let pu: ParseUnits = parse_units(amount, units).unwrap(); + let b = U256::from(pu); + assert_eq!(format!("{b:?}"), "420000"); + + // From numbers + let c = U256::from(42_u8); + assert_eq!(format!("{c:?}"), "42"); + + let d = U256::from(42_u16); + assert_eq!(format!("{d:?}"), "42"); + + let e = U256::from(42_u32); + assert_eq!(format!("{e:?}"), "42"); + + let f = U256::from(42_u64); + assert_eq!(format!("{f:?}"), "42"); + + let g = U256::from(42_u128); + assert_eq!(format!("{g:?}"), "42"); + + let h = U256::from(0x2a); + assert_eq!(format!("{h:?}"), "42"); + + let i: U256 = 42.into(); + assert_eq!(format!("{i:?}"), "42"); + + // From `Numeric` + let num: Numeric = Numeric::U256(U256::one()); + let l = U256::from(num); + assert_eq!(format!("{l:?}"), "1"); + + let num: Numeric = Numeric::Num(42); + let m = U256::from(num); + assert_eq!(format!("{m:?}"), "42"); +} diff --git a/examples/big-numbers/examples/math_operations.rs b/examples/big-numbers/examples/math_operations.rs new file mode 100644 index 00000000..45938331 --- /dev/null +++ b/examples/big-numbers/examples/math_operations.rs @@ -0,0 +1,49 @@ +use ethers::{types::U256, utils::format_units}; +use std::ops::{Div, Mul}; + +fn main() { + let a = U256::from(10); + let b = U256::from(2); + + // addition + let sum = a + b; + assert_eq!(sum, U256::from(12)); + + // subtraction + let difference = a - b; + assert_eq!(difference, U256::from(8)); + + // multiplication + let product = a * b; + assert_eq!(product, U256::from(20)); + + // division + let quotient = a / b; + assert_eq!(quotient, U256::from(5)); + + // modulo + let remainder = a % b; + assert_eq!(remainder, U256::zero()); // equivalent to `U256::from(0)` + + // exponentiation + let power = a.pow(b); + assert_eq!(power, U256::from(100)); + // powers of 10 can also be expressed like this: + let power_of_10 = U256::exp10(2); + assert_eq!(power_of_10, U256::from(100)); + + // Multiply two 'ether' numbers: + // Big numbers are integers, that can represent fixed point numbers. + // For instance, 1 ether has 18 fixed + // decimal places (1.000000000000000000), and its big number + // representation is 10^18 = 1000000000000000000. + // When we multiply such numbers we are summing up their exponents. + // So if we multiply 10^18 * 10^18 we get 10^36, that is obviously incorrect. + // In order to get the correct result we need to divide by 10^18. + let eth1 = U256::from(10_000000000000000000_u128); // 10 ether + let eth2 = U256::from(20_000000000000000000_u128); // 20 ether + let base = U256::from(10).pow(18.into()); + let mul = eth1.mul(eth2).div(base); // We also divide by 10^18 + let s: String = format_units(mul, "ether").unwrap(); + assert_eq!(s, "200.000000000000000000"); // 200 +} diff --git a/examples/big-numbers/examples/utilities.rs b/examples/big-numbers/examples/utilities.rs new file mode 100644 index 00000000..58493584 --- /dev/null +++ b/examples/big-numbers/examples/utilities.rs @@ -0,0 +1,92 @@ +use ethers::{ + types::U256, + utils::{format_units, parse_units, ParseUnits}, +}; + +fn main() { + parse_units_example(); + format_units_example(); +} + +/// DApps business logics handles big numbers in 'wei' units (i.e. sending transactions, on-chain +/// math, etc.). We provide convenient methods to map user inputs (usually in 'ether' or 'gwei') +/// into 'wei' format. +fn parse_units_example() { + let pu: ParseUnits = parse_units("1.0", "wei").unwrap(); + let num = U256::from(pu); + assert_eq!(num, U256::one()); + + let pu: ParseUnits = parse_units("1.0", "kwei").unwrap(); + let num = U256::from(pu); + assert_eq!(num, U256::from(1000)); + + let pu: ParseUnits = parse_units("1.0", "mwei").unwrap(); + let num = U256::from(pu); + assert_eq!(num, U256::from(1000000)); + + let pu: ParseUnits = parse_units("1.0", "gwei").unwrap(); + let num = U256::from(pu); + assert_eq!(num, U256::from(1000000000)); + + let pu: ParseUnits = parse_units("1.0", "szabo").unwrap(); + let num = U256::from(pu); + assert_eq!(num, U256::from(1000000000000_u128)); + + let pu: ParseUnits = parse_units("1.0", "finney").unwrap(); + let num = U256::from(pu); + assert_eq!(num, U256::from(1000000000000000_u128)); + + let pu: ParseUnits = parse_units("1.0", "ether").unwrap(); + let num = U256::from(pu); + assert_eq!(num, U256::from(1000000000000000000_u128)); + + let pu: ParseUnits = parse_units("1.0", 18).unwrap(); + let num = U256::from(pu); + assert_eq!(num, U256::from(1000000000000000000_u128)); +} + +/// DApps business logics handles big numbers in 'wei' units (i.e. sending transactions, on-chain +/// math, etc.). On the other hand it is useful to convert big numbers into user readable formats +/// when displaying on a UI. Generally dApps display numbers in 'ether' and 'gwei' units, +/// respectively for displaying amounts and gas. The `format_units` function will format a big +/// number into a user readable string. +fn format_units_example() { + // 1 ETHER = 10^18 WEI + let one_ether = U256::from(1000000000000000000_u128); + + let num: String = format_units(one_ether, "wei").unwrap(); + assert_eq!(num, "1000000000000000000.0"); + + let num: String = format_units(one_ether, "gwei").unwrap(); + assert_eq!(num, "1000000000.000000000"); + + let num: String = format_units(one_ether, "ether").unwrap(); + assert_eq!(num, "1.000000000000000000"); + + // 1 GWEI = 10^9 WEI + let one_gwei = U256::from(1000000000_u128); + + let num: String = format_units(one_gwei, 0).unwrap(); + assert_eq!(num, "1000000000.0"); + + let num: String = format_units(one_gwei, "wei").unwrap(); + assert_eq!(num, "1000000000.0"); + + let num: String = format_units(one_gwei, "kwei").unwrap(); + assert_eq!(num, "1000000.000"); + + let num: String = format_units(one_gwei, "mwei").unwrap(); + assert_eq!(num, "1000.000000"); + + let num: String = format_units(one_gwei, "gwei").unwrap(); + assert_eq!(num, "1.000000000"); + + let num: String = format_units(one_gwei, "szabo").unwrap(); + assert_eq!(num, "0.001000000000"); + + let num: String = format_units(one_gwei, "finney").unwrap(); + assert_eq!(num, "0.000001000000000"); + + let num: String = format_units(one_gwei, "ether").unwrap(); + assert_eq!(num, "0.000000001000000000"); +} diff --git a/examples/contracts/Cargo.toml b/examples/contracts/Cargo.toml new file mode 100644 index 00000000..4f0e12bd --- /dev/null +++ b/examples/contracts/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "examples-contracts" +version = "1.0.2" +authors = ["Andrea Simeoni "] +edition = "2021" + +[features] +default = ["legacy"] +legacy = [] + +[dev-dependencies] +ethers = { path = "../..", version = "1.0.0", features = ["abigen", "ethers-solc", "legacy", "rustls", "ws"] } + +eyre = "0.6" +serde = { version = "1.0.144", features = ["derive"] } +serde_json = "1.0.64" +tokio = { version = "1.18", features = ["macros"] } + + diff --git a/examples/contracts/README.md b/examples/contracts/README.md new file mode 100644 index 00000000..472592f7 --- /dev/null +++ b/examples/contracts/README.md @@ -0,0 +1,39 @@ +# Contracts +In this guide, we will go over some examples of using ethers-rs to work with contracts, including using abigen to generate Rust bindings for a contract, listening for contract events, calling contract methods, and instantiating contracts. + +## Generating Rust bindings with abigen +To use a contract with ethers-rs, you will need to generate Rust bindings using the abigen tool. abigen is included with the ethers-rs library and can be used to generate Rust bindings for any Solidity contract. + +### Generate a Rust file +This method takes a smart contract's Application Binary Interface (ABI) file and generates a Rust file to interact with it. This is useful if the smart contract is referenced in different places in a project. File generation from ABI can also be easily included as a build step of your application. + +Running the code below will generate a file called `token.rs` containing the bindings inside, which exports an `ERC20Token` struct, along with all its events and methods. Put into a `build.rs` file this will generate the bindings during cargo build. + +```rust +Abigen::new("ERC20Token", "./abi.json")?.generate()?.write_to_file("token.rs")?; +``` + +### Generate inline Rust bindings +This method takes a smart contract's solidity definition and generates inline Rust code to interact with it. This is useful for fast prototyping and for tight scoped use-cases of your contracts. Inline Rust generation uses the `abigen!` macro to expand Rust contract bindings. + +Running the code below will generate bindings for the `ERC20Token` struct, along with all its events and methods. +```rust +abigen!( + ERC20Token, + r#"[ + function approve(address spender, uint256 amount) external returns (bool) + event Transfer(address indexed from, address indexed to, uint256 value) + event Approval(address indexed owner, address indexed spender, uint256 value) + ]"#, +); +``` + +Another way to get the same result, is to provide the ABI contract's definition as follows. +```rust +abigen!(ERC20Token, "./abi.json",); +``` + +## Contract instances +## Contract methods +## Contract events + diff --git a/examples/contracts/examples/abi/IERC20.json b/examples/contracts/examples/abi/IERC20.json new file mode 100644 index 00000000..4827e872 --- /dev/null +++ b/examples/contracts/examples/abi/IERC20.json @@ -0,0 +1,153 @@ +[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [{ "name": "", "type": "string" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "guy", "type": "address" }, + { "name": "wad", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "src", "type": "address" }, + { "name": "dst", "type": "address" }, + { "name": "wad", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "wad", "type": "uint256" }], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [{ "name": "", "type": "uint8" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "", "type": "address" }], + "name": "balanceOf", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [{ "name": "", "type": "string" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "dst", "type": "address" }, + { "name": "wad", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "deposit", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { "name": "", "type": "address" }, + { "name": "", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { "payable": true, "stateMutability": "payable", "type": "fallback" }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "src", "type": "address" }, + { "indexed": true, "name": "guy", "type": "address" }, + { "indexed": false, "name": "wad", "type": "uint256" } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "src", "type": "address" }, + { "indexed": true, "name": "dst", "type": "address" }, + { "indexed": false, "name": "wad", "type": "uint256" } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "dst", "type": "address" }, + { "indexed": false, "name": "wad", "type": "uint256" } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "src", "type": "address" }, + { "indexed": false, "name": "wad", "type": "uint256" } + ], + "name": "Withdrawal", + "type": "event" + } +] diff --git a/examples/contract_abi.json b/examples/contracts/examples/abi/contract_abi.json similarity index 100% rename from examples/contract_abi.json rename to examples/contracts/examples/abi/contract_abi.json diff --git a/examples/contracts/examples/abigen.rs b/examples/contracts/examples/abigen.rs new file mode 100644 index 00000000..b29fa2b2 --- /dev/null +++ b/examples/contracts/examples/abigen.rs @@ -0,0 +1,77 @@ +use ethers::{ + prelude::{abigen, Abigen}, + providers::{Http, Provider}, + types::Address, +}; +use eyre::Result; +use std::sync::Arc; + +/// Abigen is used to generate Rust code to interact with smart contracts on the blockchain. +/// It provides a way to encode and decode data that is passed to and from smart contracts. +/// The output of abigen is Rust code, that is bound to the contract's interface, allowing +/// developers to call its methods to read/write on-chain state and subscribe to realtime events. +/// +/// The abigen tool can be used in two ways, addressing different use-cases scenarios and developer +/// taste: +/// +/// 1. **Rust file generation:** takes a smart contract's Application Binary Interface (ABI) +/// file and generates a Rust file to interact with it. This is useful if the smart contract is +/// referenced in different places in a project. File generation from ABI can also be easily +/// included as a build step of your application. +/// 2. **Rust inline generation:** takes a smart contract's solidity definition and generates inline +/// Rust code to interact with it. This is useful for fast prototyping and for tight scoped +/// use-cases of your contracts. +/// 3. **Rust inline generation from ABI:** similar to the previous point but instead of Solidity +/// code takes in input a smart contract's Application Binary Interface (ABI) file. +#[tokio::main] +async fn main() -> Result<()> { + rust_file_generation()?; + rust_inline_generation().await?; + rust_inline_generation_from_abi(); + Ok(()) +} + +fn rust_file_generation() -> Result<()> { + let base_dir = "./examples/contracts/examples/abi"; + Abigen::new("IERC20", format!("{base_dir}/IERC20.json"))? + .generate()? + .write_to_file(format!("{base_dir}/ierc20.rs"))?; + Ok(()) +} + +fn rust_inline_generation_from_abi() { + abigen!(IERC20, "./examples/contracts/examples/abi/IERC20.json"); +} + +async fn rust_inline_generation() -> Result<()> { + // The abigen! macro expands the contract's code in the current scope + // so that you can interface your Rust program with the blockchain + // counterpart of the contract. + abigen!( + IERC20, + r#"[ + function totalSupply() external view returns (uint256) + function balanceOf(address account) external view returns (uint256) + function transfer(address recipient, uint256 amount) external returns (bool) + function allowance(address owner, address spender) external view returns (uint256) + function approve(address spender, uint256 amount) external returns (bool) + function transferFrom( address sender, address recipient, uint256 amount) external returns (bool) + event Transfer(address indexed from, address indexed to, uint256 value) + event Approval(address indexed owner, address indexed spender, uint256 value) + ]"#, + ); + + const RPC_URL: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"; + const WETH_ADDRESS: &str = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; + + let provider = Provider::::try_from(RPC_URL)?; + let client = Arc::new(provider); + let address: Address = WETH_ADDRESS.parse()?; + let contract = IERC20::new(address, client); + + if let Ok(total_supply) = contract.total_supply().call().await { + println!("WETH total supply is {total_supply:?}"); + } + + Ok(()) +} diff --git a/examples/abigen.rs b/examples/contracts/examples/compile.rs similarity index 77% rename from examples/abigen.rs rename to examples/contracts/examples/compile.rs index e0886687..21b7586a 100644 --- a/examples/abigen.rs +++ b/examples/contracts/examples/compile.rs @@ -1,11 +1,14 @@ -use ethers::{contract::Abigen, solc::Solc}; +use ethers::{prelude::Abigen, solc::Solc}; +use eyre::Result; -fn main() -> eyre::Result<()> { +fn main() -> Result<()> { let mut args = std::env::args(); args.next().unwrap(); // skip program name let contract_name = args.next().unwrap_or_else(|| "SimpleStorage".to_owned()); - let contract: String = args.next().unwrap_or_else(|| "examples/contract.sol".to_owned()); + let contract: String = args + .next() + .unwrap_or_else(|| "examples/contracts/examples/contracts/contract.sol".to_owned()); println!("Generating bindings for {contract}\n"); diff --git a/examples/contract.sol b/examples/contracts/examples/contracts/contract.sol similarity index 93% rename from examples/contract.sol rename to examples/contracts/examples/contracts/contract.sol index 9d04f2f4..e9910f32 100644 --- a/examples/contract.sol +++ b/examples/contracts/examples/contracts/contract.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Unlicense pragma solidity >=0.4.24; contract SimpleStorage { diff --git a/examples/contract_human_readable.rs b/examples/contracts/examples/deploy_anvil.rs similarity index 90% rename from examples/contract_human_readable.rs rename to examples/contracts/examples/deploy_anvil.rs index d0ca18c7..7ef77176 100644 --- a/examples/contract_human_readable.rs +++ b/examples/contracts/examples/deploy_anvil.rs @@ -1,10 +1,13 @@ use ethers::{ - prelude::*, - solc::{Project, ProjectPathsConfig}, - utils::Anvil, + contract::{abigen, ContractFactory}, + core::utils::Anvil, + middleware::SignerMiddleware, + providers::{Http, Provider}, + signers::{LocalWallet, Signer}, + solc::{Artifact, Project, ProjectPathsConfig}, }; use eyre::Result; -use std::{convert::TryFrom, path::PathBuf, sync::Arc, time::Duration}; +use std::{path::PathBuf, sync::Arc, time::Duration}; // Generate the type-safe contract bindings by providing the ABI // definition in human readable format diff --git a/examples/contract_with_abi_and_bytecode.rs b/examples/contracts/examples/deploy_from_abi_and_bytecode.rs similarity index 89% rename from examples/contract_with_abi_and_bytecode.rs rename to examples/contracts/examples/deploy_from_abi_and_bytecode.rs index 2748e252..1dd91f37 100644 --- a/examples/contract_with_abi_and_bytecode.rs +++ b/examples/contracts/examples/deploy_from_abi_and_bytecode.rs @@ -1,4 +1,10 @@ -use ethers::{prelude::*, utils::Anvil}; +use ethers::{ + contract::abigen, + core::utils::Anvil, + middleware::SignerMiddleware, + providers::{Http, Provider}, + signers::{LocalWallet, Signer}, +}; use eyre::Result; use std::{convert::TryFrom, sync::Arc, time::Duration}; diff --git a/examples/contract_with_abi.rs b/examples/contracts/examples/deploy_from_solidity.rs similarity index 85% rename from examples/contract_with_abi.rs rename to examples/contracts/examples/deploy_from_solidity.rs index c77d094f..fa6fb365 100644 --- a/examples/contract_with_abi.rs +++ b/examples/contracts/examples/deploy_from_solidity.rs @@ -1,4 +1,11 @@ -use ethers::{prelude::*, utils::Anvil}; +use ethers::{ + contract::{abigen, ContractFactory}, + core::utils::Anvil, + middleware::SignerMiddleware, + providers::{Http, Provider}, + signers::{LocalWallet, Signer}, + solc::Solc, +}; use eyre::Result; use std::{convert::TryFrom, path::Path, sync::Arc, time::Duration}; @@ -6,7 +13,7 @@ use std::{convert::TryFrom, path::Path, sync::Arc, time::Duration}; // definition abigen!( SimpleContract, - "./examples/contract_abi.json", + "./examples/contracts/examples/abi/contract_abi.json", event_derives(serde::Deserialize, serde::Serialize) ); @@ -17,8 +24,8 @@ async fn main() -> Result<()> { let anvil = Anvil::new().spawn(); // set the path to the contract, `CARGO_MANIFEST_DIR` points to the directory containing the - // manifest of `ethers`. which will be `../` relative to this file - let source = Path::new(&env!("CARGO_MANIFEST_DIR")).join("examples/contract.sol"); + // manifest of `example/contracts`. which will be `../` relative to this file + let source = Path::new(&env!("CARGO_MANIFEST_DIR")).join("examples/contracts/contract.sol"); let compiled = Solc::default().compile_source(source).expect("Could not compile contracts"); let (abi, bytecode, _runtime_bytecode) = compiled.find("SimpleStorage").expect("could not find contract").into_parts_or_default(); diff --git a/examples/moonbeam_with_abi.rs b/examples/contracts/examples/deploy_moonbeam.rs similarity index 95% rename from examples/moonbeam_with_abi.rs rename to examples/contracts/examples/deploy_moonbeam.rs index e17500bc..a26464c8 100644 --- a/examples/moonbeam_with_abi.rs +++ b/examples/contracts/examples/deploy_moonbeam.rs @@ -1,8 +1,8 @@ -use ethers::prelude::*; +use ethers::contract::abigen; abigen!( SimpleContract, - "./examples/contract_abi.json", + "./examples/contracts/examples/abi/contract_abi.json", event_derives(serde::Deserialize, serde::Serialize) ); @@ -19,7 +19,9 @@ abigen!( #[tokio::main] #[cfg(feature = "legacy")] async fn main() -> eyre::Result<()> { + use ethers::prelude::*; use std::{convert::TryFrom, path::Path, sync::Arc, time::Duration}; + const MOONBEAM_DEV_ENDPOINT: &str = "http://localhost:9933"; // set the path to the contract, `CARGO_MANIFEST_DIR` points to the directory containing the diff --git a/examples/contracts/examples/events.rs b/examples/contracts/examples/events.rs new file mode 100644 index 00000000..4039d883 --- /dev/null +++ b/examples/contracts/examples/events.rs @@ -0,0 +1,68 @@ +use ethers::{ + contract::abigen, + core::types::Address, + providers::{Provider, StreamExt, Ws}, +}; +use eyre::Result; +use std::sync::Arc; + +abigen!( + IERC20, + r#"[ + event Transfer(address indexed from, address indexed to, uint256 value) + event Approval(address indexed owner, address indexed spender, uint256 value) + ]"#, +); + +const WSS_URL: &str = "wss://mainnet.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27"; +const WETH_ADDRESS: &str = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; + +#[tokio::main] +async fn main() -> Result<()> { + let provider = Provider::::connect(WSS_URL).await?; + let client = Arc::new(provider); + let address: Address = WETH_ADDRESS.parse()?; + let contract = IERC20::new(address, client); + + listen_all_events(&contract).await?; + listen_specific_events(&contract).await?; + + Ok(()) +} + +/// Given a contract instance, subscribe to all possible events. +/// This allows to centralize the event handling logic and dispatch +/// proper actions. +/// +/// Note that all event bindings have been generated +/// by abigen. Feel free to investigate the abigen expanded code to +/// better understand types and functionalities. +async fn listen_all_events(contract: &IERC20>) -> Result<()> { + let events = contract.events().from_block(16232696); + let mut stream = events.stream().await?.take(1); + + while let Some(Ok(evt)) = stream.next().await { + match evt { + IERC20Events::ApprovalFilter(f) => println!("{f:?}"), + IERC20Events::TransferFilter(f) => println!("{f:?}"), + } + } + + Ok(()) +} + +/// Given a contract instance subscribe to a single type of event. +/// +/// Note that all event bindings have been generated +/// by abigen. Feel free to investigate the abigen expanded code to +/// better understand types and functionalities. +async fn listen_specific_events(contract: &IERC20>) -> Result<()> { + let events = contract.event::().from_block(16232696); + let mut stream = events.stream().await?.take(1); + + while let Some(Ok(f)) = stream.next().await { + println!("ApprovalFilter event: {f:?}"); + } + + Ok(()) +} diff --git a/examples/subscribe_contract_events_with_meta.rs b/examples/contracts/examples/events_with_meta.rs similarity index 80% rename from examples/subscribe_contract_events_with_meta.rs rename to examples/contracts/examples/events_with_meta.rs index c81b4da8..ceb0c68e 100644 --- a/examples/subscribe_contract_events_with_meta.rs +++ b/examples/contracts/examples/events_with_meta.rs @@ -1,4 +1,8 @@ -use ethers::prelude::*; +use ethers::{ + contract::abigen, + core::types::Address, + providers::{Provider, StreamExt, Ws}, +}; use eyre::Result; use std::sync::Arc; @@ -11,9 +15,6 @@ abigen!( ]"#, ); -// In order to run this example you need to include Ws and TLS features -// Run this example with -// `cargo run -p ethers --example subscribe_contract_events_with_meta --features="ws","rustls"` #[tokio::main] async fn main() -> Result<()> { let client = @@ -27,8 +28,8 @@ async fn main() -> Result<()> { let weth = ERC20::new(address, Arc::clone(&client)); // Subscribe Transfer events - let events = weth.events(); - let mut stream = events.stream().await?.with_meta(); + let events = weth.events().from_block(16232698); + let mut stream = events.stream().await?.with_meta().take(1); while let Some(Ok((event, meta))) = stream.next().await { println!("src: {:?}, dst: {:?}, wad: {:?}", event.src, event.dst, event.wad); diff --git a/examples/contracts/examples/instances.rs b/examples/contracts/examples/instances.rs new file mode 100644 index 00000000..7f755fb7 --- /dev/null +++ b/examples/contracts/examples/instances.rs @@ -0,0 +1,2 @@ +#[tokio::main] +async fn main() {} diff --git a/examples/contracts/examples/methods.rs b/examples/contracts/examples/methods.rs new file mode 100644 index 00000000..7f755fb7 --- /dev/null +++ b/examples/contracts/examples/methods.rs @@ -0,0 +1,2 @@ +#[tokio::main] +async fn main() {} diff --git a/examples/contract_with_abi_and_structs.rs b/examples/contracts/examples/methods_abi_and_structs.rs similarity index 82% rename from examples/contract_with_abi_and_structs.rs rename to examples/contracts/examples/methods_abi_and_structs.rs index b74c9ea9..87b08c52 100644 --- a/examples/contract_with_abi_and_structs.rs +++ b/examples/contracts/examples/methods_abi_and_structs.rs @@ -1,7 +1,17 @@ //! Main entry point for ContractMonitor -use ethers::{prelude::*, utils::Anvil}; -use std::{convert::TryFrom, sync::Arc, time::Duration}; +use std::{sync::Arc, time::Duration}; + +use ethers::{ + contract::abigen, + core::{ + types::{Address, U256}, + utils::Anvil, + }, + middleware::SignerMiddleware, + providers::{Http, Provider}, + signers::LocalWallet, +}; abigen!(VerifierContract, "ethers-contract/tests/solidity-contracts/verifier_abi.json"); diff --git a/examples/ethers-wasm/Cargo.toml b/examples/ethers-wasm/Cargo.toml index 181e0f81..6ca4f575 100644 --- a/examples/ethers-wasm/Cargo.toml +++ b/examples/ethers-wasm/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ethers-wasm" version = "0.1.0" -edition = "2018" +edition = "2021" rust-version = "1.64" authors = ["Matthias Seitz "] license = "MIT OR Apache-2.0" diff --git a/examples/ethers-wasm/abi/contract_abi.json b/examples/ethers-wasm/abi/contract_abi.json new file mode 100644 index 00000000..12de10c8 --- /dev/null +++ b/examples/ethers-wasm/abi/contract_abi.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"string","name":"value","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"author","type":"address"},{"indexed":false,"internalType":"string","name":"oldValue","type":"string"},{"indexed":false,"internalType":"string","name":"newValue","type":"string"}],"name":"ValueChanged","type":"event"},{"inputs":[],"name":"getValue","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"value","type":"string"}],"name":"setValue","outputs":[],"stateMutability":"nonpayable","type":"function"}] diff --git a/examples/ethers-wasm/src/lib.rs b/examples/ethers-wasm/src/lib.rs index 2e18ad03..273836ce 100644 --- a/examples/ethers-wasm/src/lib.rs +++ b/examples/ethers-wasm/src/lib.rs @@ -24,7 +24,7 @@ macro_rules! log { abigen!( SimpleContract, - "./../contract_abi.json", + "./abi/contract_abi.json", event_derives(serde::Deserialize, serde::Serialize) ); diff --git a/examples/ethers-wasm/tests/contract_with_abi.rs b/examples/ethers-wasm/tests/contract_with_abi.rs index 5530df67..df080b38 100644 --- a/examples/ethers-wasm/tests/contract_with_abi.rs +++ b/examples/ethers-wasm/tests/contract_with_abi.rs @@ -16,7 +16,7 @@ wasm_bindgen_test_configure!(run_in_browser); // definition in human readable format abigen!( SimpleContract, - "../contract_abi.json", + "./abi/contract_abi.json", event_derives(serde::Deserialize, serde::Serialize) ); diff --git a/examples/events/Cargo.toml b/examples/events/Cargo.toml new file mode 100644 index 00000000..4330bdf7 --- /dev/null +++ b/examples/events/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "examples-events" +version = "1.0.2" +authors = ["Andrea Simeoni "] +edition = "2021" + +[dev-dependencies] +ethers = { path = "../..", version = "1.0.0" } + +eyre = "0.6" +serde = { version = "1.0.144", features = ["derive"] } +serde_json = "1.0.64" +tokio = { version = "1.18", features = ["macros"] } \ No newline at end of file diff --git a/examples/events/README.md b/examples/events/README.md new file mode 100644 index 00000000..e69de29b diff --git a/examples/events/examples/placeholder.rs b/examples/events/examples/placeholder.rs new file mode 100644 index 00000000..7f755fb7 --- /dev/null +++ b/examples/events/examples/placeholder.rs @@ -0,0 +1,2 @@ +#[tokio::main] +async fn main() {} diff --git a/examples/geth_trace_call.rs b/examples/geth_trace_call.rs new file mode 100644 index 00000000..00851951 --- /dev/null +++ b/examples/geth_trace_call.rs @@ -0,0 +1,26 @@ +use ethers::prelude::*; +use eyre::Result; +use std::str::FromStr; + +/// use `debug_traceCall` to fetch traces +/// requires, a valid endpoint in `RPC_URL` env var that supports `debug_traceCall` +#[tokio::main] +async fn main() -> Result<()> { + if let Ok(url) = std::env::var("RPC_URL") { + let client = Provider::::try_from(url)?; + let tx = TransactionRequest::new().from(Address::from_str("0xdeadbeef29292929192939494959594933929292").unwrap()).to(Address::from_str("0xde929f939d939d393f939393f93939f393929023").unwrap()).gas("0x7a120").data(Bytes::from_str("0xf00d4b5d00000000000000000000000001291230982139282304923482304912923823920000000000000000000000001293123098123928310239129839291010293810").unwrap()); + let block = BlockId::from(16213100); + let options: GethDebugTracingCallOptions = GethDebugTracingCallOptions { + tracing_options: GethDebugTracingOptions { + disable_storage: Some(true), + enable_memory: Some(false), + tracer: Some(GethDebugTracerType::CallTracer), + ..Default::default() + }, + }; + let traces = client.debug_trace_call(tx, Some(block), options).await?; + println!("{traces:?}"); + } + + Ok(()) +} diff --git a/examples/middleware/Cargo.toml b/examples/middleware/Cargo.toml new file mode 100644 index 00000000..0c6eb85d --- /dev/null +++ b/examples/middleware/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "examples-middleware" +version = "1.0.2" +authors = ["Andrea Simeoni "] +edition = "2021" + +[dev-dependencies] +ethers = { path = "../..", version = "1.0.0", features = ["rustls", "ws"] } + +async-trait = { version = "0.1.50", default-features = false } +eyre = "0.6" +serde_json = "1.0.61" +thiserror = { version = "1.0", default-features = false } +tokio = { version = "1.18", features = ["macros", "rt", "rt-multi-thread"] } + diff --git a/examples/middleware/README.md b/examples/middleware/README.md new file mode 100644 index 00000000..e69de29b diff --git a/examples/middleware/examples/builder.rs b/examples/middleware/examples/builder.rs new file mode 100644 index 00000000..c9fb058a --- /dev/null +++ b/examples/middleware/examples/builder.rs @@ -0,0 +1,62 @@ +use ethers::{ + core::types::BlockNumber, + middleware::{ + gas_escalator::{Frequency, GasEscalatorMiddleware, GeometricGasPrice}, + gas_oracle::{GasNow, GasOracleMiddleware}, + MiddlewareBuilder, NonceManagerMiddleware, SignerMiddleware, + }, + providers::{Http, Middleware, Provider}, + signers::{LocalWallet, Signer}, +}; +use std::convert::TryFrom; + +const RPC_URL: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"; +const SIGNING_KEY: &str = "fdb33e2105f08abe41a8ee3b758726a31abdd57b7a443f470f23efce853af169"; + +/// In ethers-rs, middleware is a way to customize the behavior of certain aspects of the library by +/// injecting custom logic into the process of sending transactions and interacting with contracts +/// on the Ethereum blockchain. The MiddlewareBuilder trait provides a way to define a chain of +/// middleware that will be called at different points in this process, allowing you to customize +/// the behavior of the Provider based on your needs. +#[tokio::main] +async fn main() { + builder_example().await; + builder_example_raw_wrap().await; +} + +async fn builder_example() { + let signer = SIGNING_KEY.parse::().unwrap(); + let address = signer.address(); + let escalator = GeometricGasPrice::new(1.125, 60_u64, None::); + let gas_oracle = GasNow::new(); + + let provider = Provider::::try_from(RPC_URL) + .unwrap() + .wrap_into(|p| GasEscalatorMiddleware::new(p, escalator, Frequency::PerBlock)) + .gas_oracle(gas_oracle) + .with_signer(signer) + .nonce_manager(address); // Outermost layer + + match provider.get_block(BlockNumber::Latest).await { + Ok(Some(block)) => println!("{:?}", block.number), + _ => println!("Unable to get latest block"), + } +} + +async fn builder_example_raw_wrap() { + let signer = SIGNING_KEY.parse::().unwrap(); + let address = signer.address(); + let escalator = GeometricGasPrice::new(1.125, 60_u64, None::); + + let provider = Provider::::try_from(RPC_URL) + .unwrap() + .wrap_into(|p| GasEscalatorMiddleware::new(p, escalator, Frequency::PerBlock)) + .wrap_into(|p| SignerMiddleware::new(p, signer)) + .wrap_into(|p| GasOracleMiddleware::new(p, GasNow::new())) + .wrap_into(|p| NonceManagerMiddleware::new(p, address)); // Outermost layer + + match provider.get_block(BlockNumber::Latest).await { + Ok(Some(block)) => println!("{:?}", block.number), + _ => println!("Unable to get latest block"), + } +} diff --git a/examples/middleware/examples/create_custom_middleware.rs b/examples/middleware/examples/create_custom_middleware.rs new file mode 100644 index 00000000..56a4a21d --- /dev/null +++ b/examples/middleware/examples/create_custom_middleware.rs @@ -0,0 +1,156 @@ +use async_trait::async_trait; +use ethers::{ + core::{ + types::{transaction::eip2718::TypedTransaction, BlockId, TransactionRequest, U256}, + utils::{parse_units, Anvil}, + }, + middleware::MiddlewareBuilder, + providers::{FromErr, Http, Middleware, PendingTransaction, Provider}, + signers::{LocalWallet, Signer}, +}; +use thiserror::Error; + +/// This example demonstrates the mechanisms for creating custom middlewares in ethers-rs. +/// The example includes explanations of the process and code snippets to illustrate the +/// concepts. It is intended for developers who want to learn how to customize the behavior of +/// ethers-rs providers by creating and using custom middlewares. +/// +/// This custom middleware increases the gas value of transactions sent through an ethers-rs +/// provider by a specified percentage and will be called for each transaction before it is sent. +/// This can be useful if you want to ensure that transactions have a higher gas value than the +/// estimated, in order to improve the chances of them not to run out of gas when landing on-chain. +#[derive(Debug)] +struct GasMiddleware { + inner: M, + /// This value is used to raise the gas value before sending transactions + contingency: U256, +} + +/// Contingency is expressed with 4 units +/// e.g. +/// 50% => 1 + 0.5 => 15000 +/// 20% => 1 + 0.2 => 12000 +/// 1% => 1 + 0.01 => 10100 +const CONTINGENCY_UNITS: usize = 4; + +impl GasMiddleware +where + M: Middleware, +{ + /// Creates an instance of GasMiddleware + /// `ìnner` the inner Middleware + /// `perc` This is an unsigned integer representing the percentage increase in the amount of gas + /// to be used for the transaction. The percentage is relative to the gas value specified in the + /// transaction. Valid contingency values are in range 1..=50. Otherwise a custom middleware + /// error is raised. + pub fn new(inner: M, perc: u32) -> Result> { + let contingency = match perc { + 0 => Err(GasMiddlewareError::TooLowContingency(perc))?, + 51.. => Err(GasMiddlewareError::TooHighContingency(perc))?, + 1..=50 => { + let decimals = 2; + let perc = U256::from(perc) * U256::exp10(decimals); // e.g. 50 => 5000 + let one = parse_units(1, CONTINGENCY_UNITS).unwrap(); + let one = U256::from(one); + one + perc // e.g. 50% => 1 + 0.5 => 10000 + 5000 => 15000 + } + }; + + Ok(Self { inner, contingency }) + } +} + +/// Let's implement the `Middleware` trait for our custom middleware. +/// All trait functions are derived automatically, so we just need to +/// override the needed functions. +#[async_trait] +impl Middleware for GasMiddleware +where + M: Middleware, +{ + type Error = GasMiddlewareError; + type Provider = M::Provider; + type Inner = M; + + fn inner(&self) -> &M { + &self.inner + } + + /// In this function we bump the transaction gas value by the specified percentage + /// This can raise a custom middleware error if a gas amount was not set for + /// the transaction. + async fn send_transaction + Send + Sync>( + &self, + tx: T, + block: Option, + ) -> Result, Self::Error> { + let mut tx: TypedTransaction = tx.into(); + + let curr_gas: U256 = match tx.gas() { + Some(gas) => gas.to_owned(), + None => Err(GasMiddlewareError::NoGasSetForTransaction)?, + }; + + println!("Original transaction gas: {curr_gas:?} wei"); + let units: U256 = U256::exp10(CONTINGENCY_UNITS); + let raised_gas: U256 = (curr_gas * self.contingency) / units; + tx.set_gas(raised_gas); + println!("Raised transaction gas: {raised_gas:?} wei"); + + // Dispatch the call to the inner layer + self.inner().send_transaction(tx, block).await.map_err(FromErr::from) + } +} + +/// This example demonstrates how to handle errors in custom middlewares. It shows how to define +/// custom error types, use them in middleware implementations, and how to propagate the errors +/// through the middleware chain. This is intended for developers who want to create custom +/// middlewares that can handle and propagate errors in a consistent and robust way. +#[derive(Error, Debug)] +pub enum GasMiddlewareError { + /// Thrown when the internal middleware errors + #[error("{0}")] + MiddlewareError(M::Error), + /// Specific errors of this GasMiddleware. + /// Please refer to the `thiserror` crate for + /// further docs. + #[error("{0}")] + TooHighContingency(u32), + #[error("{0}")] + TooLowContingency(u32), + #[error("Cannot raise gas! Gas value not provided for this transaction.")] + NoGasSetForTransaction, +} + +impl FromErr for GasMiddlewareError { + fn from(src: M::Error) -> Self { + GasMiddlewareError::MiddlewareError(src) + } +} + +#[tokio::main] +async fn main() -> eyre::Result<()> { + let anvil = Anvil::new().spawn(); + + let wallet: LocalWallet = anvil.keys()[0].clone().into(); + let wallet2: LocalWallet = anvil.keys()[1].clone().into(); + let signer = wallet.with_chain_id(anvil.chain_id()); + + let gas_raise_perc = 50; // 50%; + let provider = Provider::::try_from(anvil.endpoint())? + .with_signer(signer) + .wrap_into(|s| GasMiddleware::new(s, gas_raise_perc).unwrap()); + + let gas = 15000; + let tx = TransactionRequest::new().to(wallet2.address()).value(10000).gas(gas); + + let pending_tx = provider.send_transaction(tx, None).await?; + + let receipt = pending_tx.await?.ok_or_else(|| eyre::format_err!("tx dropped from mempool"))?; + let tx = provider.get_transaction(receipt.transaction_hash).await?; + + println!("Sent tx: {}\n", serde_json::to_string(&tx)?); + println!("Tx receipt: {}", serde_json::to_string(&receipt)?); + + Ok(()) +} diff --git a/examples/middleware/examples/gas_escalator.rs b/examples/middleware/examples/gas_escalator.rs new file mode 100644 index 00000000..c60d304c --- /dev/null +++ b/examples/middleware/examples/gas_escalator.rs @@ -0,0 +1,59 @@ +use ethers::{ + core::{types::TransactionRequest, utils::Anvil}, + middleware::gas_escalator::*, + providers::{Http, Middleware, Provider}, +}; +use eyre::Result; + +/// The gas escalator middleware in ethers-rs is designed to automatically increase the gas cost of +/// transactions if they get stuck in the mempool. This can be useful if you want to +/// ensure that transactions are processed in a timely manner without having to manually adjust the +/// gas cost yourself. +#[tokio::main] +async fn main() -> Result<()> { + let every_secs: u64 = 60; + let max_price: Option = None; + + // Linearly increase gas price: + // Start with `initial_price`, then increase it by fixed amount `increase_by` every `every_secs` + // seconds until the transaction gets confirmed. There is an optional upper limit. + let increase_by: i32 = 100; + let linear_escalator = LinearGasPrice::new(increase_by, every_secs, max_price); + send_escalating_transaction(linear_escalator).await?; + + // Geometrically increase gas price: + // Start with `initial_price`, then increase it every 'every_secs' seconds by a fixed + // coefficient. Coefficient defaults to 1.125 (12.5%), the minimum increase for Parity to + // replace a transaction. Coefficient can be adjusted, and there is an optional upper limit. + let coefficient: f64 = 1.125; + let geometric_escalator = GeometricGasPrice::new(coefficient, every_secs, max_price); + send_escalating_transaction(geometric_escalator).await?; + + Ok(()) +} + +async fn send_escalating_transaction(escalator: E) -> Result<()> +where + E: GasEscalator + Clone + 'static, +{ + // Spawn local node + let anvil = Anvil::new().spawn(); + let endpoint = anvil.endpoint(); + + // Connect to the node + let provider = Provider::::try_from(endpoint)?; + let provider = GasEscalatorMiddleware::new(provider, escalator, Frequency::PerBlock); + + let accounts = provider.get_accounts().await?; + let from = accounts[0]; + let to = accounts[1]; + let tx = TransactionRequest::new().from(from).to(to).value(1000); + + // Bumps the gas price until transaction gets mined + let pending_tx = provider.send_transaction(tx, None).await?; + let receipt = pending_tx.await?; + + println!("{receipt:?}"); + + Ok(()) +} diff --git a/examples/middleware/examples/gas_oracle.rs b/examples/middleware/examples/gas_oracle.rs new file mode 100644 index 00000000..95105aeb --- /dev/null +++ b/examples/middleware/examples/gas_oracle.rs @@ -0,0 +1,88 @@ +use ethers::{ + core::types::Chain, + etherscan::Client, + middleware::gas_oracle::{ + BlockNative, Etherscan, GasCategory, GasNow, GasOracle, Polygon, ProviderOracle, + }, + providers::{Http, Provider}, +}; + +/// In Ethereum, the "gas" of a transaction refers to the amount of computation required to execute +/// the transaction on the blockchain. Gas is typically measured in units of "gas," and the cost of +/// a transaction is determined by the amount of gas it consumes. +/// +/// A "gas oracle" is a tool or service that provides information about the current price of gas on +/// the Ethereum network. Gas oracles are often used to help determine the appropriate amount of gas +/// to include in a transaction, in order to ensure that it will be processed in a timely manner +/// without running out of gas. +/// +/// Ethers-rs includes a feature called "gas oracle middleware" that allows you to customize the +/// behavior of the library when it comes to determining the gas cost of transactions. +#[tokio::main] +async fn main() { + blocknative().await; + etherscan().await; + gas_now().await; + polygon().await; + provider_oracle().await; + //etherchain().await; // FIXME: Etherchain URL is broken (Http 404) +} + +async fn blocknative() { + let api_key: Option = std::env::var("BLOCK_NATIVE_API_KEY").ok(); + let oracle = BlockNative::new(api_key).category(GasCategory::Fastest); + match oracle.fetch().await { + Ok(gas_price) => println!("[Blocknative]: Gas price is {gas_price:?}"), + Err(e) => panic!("[Blocknative]: Cannot estimate gas: {e:?}"), + } +} + +async fn etherscan() { + let chain = Chain::Mainnet; + let api_key: String = std::env::var("ETHERSCAN_API_KEY_ETHEREUM").expect("Provide an API key"); + if let Ok(client) = Client::new(chain, api_key) { + let oracle = Etherscan::new(client).category(GasCategory::Fast); + match oracle.fetch().await { + Ok(gas_price) => println!("[Etherscan]: Gas price is {gas_price:?}"), + Err(e) => panic!("[Etherscan]: Cannot estimate gas: {e:?}"), + } + } +} + +async fn gas_now() { + let oracle = GasNow::new().category(GasCategory::Fast); + match oracle.fetch().await { + Ok(gas_price) => println!("[GasNow]: Gas price is {gas_price:?}"), + Err(e) => panic!("[GasNow]: Cannot estimate gas: {e:?}"), + } +} + +async fn polygon() { + let chain = Chain::Polygon; + if let Ok(oracle) = Polygon::new(chain) { + match oracle.category(GasCategory::SafeLow).fetch().await { + Ok(gas_price) => println!("[Polygon]: Gas price is {gas_price:?}"), + Err(e) => panic!("[Polygon]: Cannot estimate gas: {e:?}"), + } + } +} + +async fn provider_oracle() { + const RPC_URL: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"; + let provider = Provider::::try_from(RPC_URL).unwrap(); + let oracle = ProviderOracle::new(provider); + match oracle.fetch().await { + Ok(gas_price) => println!("[Provider oracle]: Gas price is {gas_price:?}"), + Err(e) => panic!("[Provider oracle]: Cannot estimate gas: {e:?}"), + } +} + +/* +// FIXME: Etherchain URL is broken (Http 404) +async fn etherchain() { + let oracle = Etherchain::new().category(GasCategory::Standard); + match oracle.fetch().await { + Ok(gas_price) => println!("[Etherchain]: Gas price is {gas_price:?}"), + Err(e) => panic!("[Etherchain]: Cannot estimate gas: {e:?}"), + } +}*/ diff --git a/examples/middleware/examples/nonce_manager.rs b/examples/middleware/examples/nonce_manager.rs new file mode 100644 index 00000000..e76bcc1c --- /dev/null +++ b/examples/middleware/examples/nonce_manager.rs @@ -0,0 +1,46 @@ +use ethers::{ + core::{ + types::{BlockNumber, TransactionRequest}, + utils::Anvil, + }, + middleware::MiddlewareBuilder, + providers::{Http, Middleware, Provider}, +}; +use eyre::Result; + +/// In Ethereum, the nonce of a transaction is a number that represents the number of transactions +/// that have been sent from a particular account. The nonce is used to ensure that transactions are +/// processed in the order they are intended, and to prevent the same transaction from being +/// processed multiple times. +/// +/// The nonce manager in ethers-rs is a middleware that helps you manage the nonce +/// of transactions by keeping track of the current nonce for a given account and automatically +/// incrementing it as needed. This can be useful if you want to ensure that transactions are sent +/// in the correct order, or if you want to avoid having to manually manage the nonce yourself. +#[tokio::main] +async fn main() -> Result<()> { + let anvil = Anvil::new().spawn(); + let endpoint = anvil.endpoint(); + + let provider = Provider::::try_from(endpoint)?; + let accounts = provider.get_accounts().await?; + let account = accounts[0]; + let to = accounts[1]; + let tx = TransactionRequest::new().from(account).to(to).value(1000); + + let nonce_manager = provider.nonce_manager(account); + + let curr_nonce = nonce_manager + .get_transaction_count(account, Some(BlockNumber::Pending.into())) + .await? + .as_u64(); + + assert_eq!(curr_nonce, 0); + + nonce_manager.send_transaction(tx, None).await?; + let next_nonce = nonce_manager.next().as_u64(); + + assert_eq!(next_nonce, 1); + + Ok(()) +} diff --git a/examples/middleware/examples/policy.rs b/examples/middleware/examples/policy.rs new file mode 100644 index 00000000..3e2e0a4b --- /dev/null +++ b/examples/middleware/examples/policy.rs @@ -0,0 +1,39 @@ +use ethers::{ + core::{types::TransactionRequest, utils::Anvil}, + middleware::{ + policy::{PolicyMiddlewareError, RejectEverything}, + MiddlewareBuilder, PolicyMiddleware, + }, + providers::{Http, Middleware, Provider}, +}; +use eyre::Result; + +/// Policy middleware is a way to inject custom logic into the process of sending transactions and +/// interacting with contracts on the Ethereum blockchain. It allows you to define rules or policies +/// that should be followed when performing these actions, and to customize the behavior of the +/// library based on these policies. +#[tokio::main] +async fn main() -> Result<()> { + let anvil = Anvil::new().spawn(); + let endpoint = anvil.endpoint(); + + let provider = Provider::::try_from(endpoint)?; + + let accounts = provider.get_accounts().await?; + let account = accounts[0]; + let to = accounts[1]; + let tx = TransactionRequest::new().from(account).to(to).value(1000); + + let policy = RejectEverything; + let policy_middleware = provider.wrap_into(|p| PolicyMiddleware::new(p, policy)); + + match policy_middleware.send_transaction(tx, None).await { + Err(e) => { + // Given the RejectEverything policy, we expect to execute this branch + assert!(matches!(e, PolicyMiddlewareError::PolicyError(()))) + } + _ => panic!("We don't expect this to happen!"), + } + + Ok(()) +} diff --git a/examples/middleware/examples/signer.rs b/examples/middleware/examples/signer.rs new file mode 100644 index 00000000..38ad057c --- /dev/null +++ b/examples/middleware/examples/signer.rs @@ -0,0 +1,40 @@ +use ethers::{ + core::{types::TransactionRequest, utils::Anvil}, + middleware::SignerMiddleware, + providers::{Http, Middleware, Provider}, + signers::{LocalWallet, Signer}, +}; +use eyre::Result; +use std::convert::TryFrom; + +/// In Ethereum, transactions must be signed with a private key before they can be broadcast to the +/// network. Ethers-rs provides a way to customize this process by allowing +/// you to define a signer, called to sign transactions before they are sent. +#[tokio::main] +async fn main() -> Result<()> { + let anvil = Anvil::new().spawn(); + + let wallet: LocalWallet = anvil.keys()[0].clone().into(); + let wallet2: LocalWallet = anvil.keys()[1].clone().into(); + + // connect to the network + let provider = Provider::::try_from(anvil.endpoint())?; + + // connect the wallet to the provider + let client = SignerMiddleware::new(provider, wallet.with_chain_id(anvil.chain_id())); + + // craft the transaction + let tx = TransactionRequest::new().to(wallet2.address()).value(10000); + + // send it! + let pending_tx = client.send_transaction(tx, None).await?; + + // get the mined tx + let receipt = pending_tx.await?.ok_or_else(|| eyre::format_err!("tx dropped from mempool"))?; + let tx = client.get_transaction(receipt.transaction_hash).await?; + + println!("Sent tx: {}\n", serde_json::to_string(&tx)?); + println!("Tx receipt: {}", serde_json::to_string(&receipt)?); + + Ok(()) +} diff --git a/examples/middleware/examples/timelag.rs b/examples/middleware/examples/timelag.rs new file mode 100644 index 00000000..7f755fb7 --- /dev/null +++ b/examples/middleware/examples/timelag.rs @@ -0,0 +1,2 @@ +#[tokio::main] +async fn main() {} diff --git a/examples/middleware/examples/transformer.rs b/examples/middleware/examples/transformer.rs new file mode 100644 index 00000000..7f755fb7 --- /dev/null +++ b/examples/middleware/examples/transformer.rs @@ -0,0 +1,2 @@ +#[tokio::main] +async fn main() {} diff --git a/examples/providers/Cargo.toml b/examples/providers/Cargo.toml new file mode 100644 index 00000000..71a92210 --- /dev/null +++ b/examples/providers/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "examples-providers" +version = "1.0.2" +authors = ["Andrea Simeoni "] +edition = "2021" + +[features] +default = ["ipc"] +ipc = [] + +[dev-dependencies] +ethers = { path = "../..", version = "1.0.0", features = ["abigen", "ipc", "rustls", "ws"] } + +eyre = "0.6" +serde = { version = "1.0.144", features = ["derive"] } +serde_json = "1.0.64" +tokio = { version = "1.18", features = ["macros"] } diff --git a/examples/providers/README.md b/examples/providers/README.md new file mode 100644 index 00000000..e69de29b diff --git a/examples/ipc.rs b/examples/providers/examples/ipc.rs similarity index 100% rename from examples/ipc.rs rename to examples/providers/examples/ipc.rs diff --git a/examples/quorum.rs b/examples/providers/examples/quorum.rs similarity index 85% rename from examples/quorum.rs rename to examples/providers/examples/quorum.rs index 95f62af1..a1d50c8c 100644 --- a/examples/quorum.rs +++ b/examples/providers/examples/quorum.rs @@ -1,11 +1,15 @@ //! Example usage for the `QuorumProvider` that requests multiple backends and only returns //! a value if the configured `Quorum` was reached. -use ethers::{prelude::*, utils::Anvil}; +use ethers::{ + core::utils::Anvil, + providers::{Http, Middleware, Provider, Quorum, QuorumProvider, WeightedProvider, Ws}, +}; +use eyre::Result; use std::{str::FromStr, time::Duration}; #[tokio::main] -async fn main() -> eyre::Result<()> { +async fn main() -> Result<()> { let anvil = Anvil::new().spawn(); // create a quorum provider with some providers diff --git a/examples/rw.rs b/examples/providers/examples/rw.rs similarity index 76% rename from examples/rw.rs rename to examples/providers/examples/rw.rs index 89733a8f..89f1f439 100644 --- a/examples/rw.rs +++ b/examples/providers/examples/rw.rs @@ -1,11 +1,15 @@ //! Example usage for the `RwClient` that uses a didicated client to send transaction and nother one //! for read ops -use ethers::{prelude::*, utils::Anvil}; +use ethers::{ + core::utils::Anvil, + providers::{Http, Middleware, Provider, Ws}, +}; +use eyre::Result; use std::{str::FromStr, time::Duration}; #[tokio::main] -async fn main() -> eyre::Result<()> { +async fn main() -> Result<()> { let anvil = Anvil::new().spawn(); let http = Http::from_str(&anvil.endpoint())?; diff --git a/examples/queries/Cargo.toml b/examples/queries/Cargo.toml new file mode 100644 index 00000000..fa92ea52 --- /dev/null +++ b/examples/queries/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "examples-queries" +version = "1.0.2" +authors = ["Andrea Simeoni "] +edition = "2021" + +[dev-dependencies] +ethers = { path = "../..", version = "1.0.0", features = ["abigen", "rustls", "ws"] } + +eyre = "0.6" +serde = { version = "1.0.144", features = ["derive"] } +serde_json = "1.0.64" +tokio = { version = "1.18", features = ["macros"] } \ No newline at end of file diff --git a/examples/queries/README.md b/examples/queries/README.md new file mode 100644 index 00000000..e69de29b diff --git a/examples/paginated_logs.rs b/examples/queries/examples/paginated_logs.rs similarity index 84% rename from examples/paginated_logs.rs rename to examples/queries/examples/paginated_logs.rs index 9f94f9e0..563b90f1 100644 --- a/examples/paginated_logs.rs +++ b/examples/queries/examples/paginated_logs.rs @@ -1,4 +1,10 @@ -use ethers::{abi::AbiDecode, prelude::*, providers::Middleware}; +use ethers::{ + core::{ + abi::AbiDecode, + types::{BlockNumber, Filter, U256}, + }, + providers::{Middleware, Provider, StreamExt, Ws}, +}; use eyre::Result; use std::sync::Arc; @@ -15,7 +21,7 @@ async fn main() -> Result<()> { let erc20_transfer_filter = Filter::new().from_block(last_block - 10000).event("Transfer(address,address,uint256)"); - let mut stream = client.get_logs_paginated(&erc20_transfer_filter, 10); + let mut stream = client.get_logs_paginated(&erc20_transfer_filter, 10).take(5); while let Some(res) = stream.next().await { let log = res?; diff --git a/examples/uniswapv2.rs b/examples/queries/examples/uniswapv2_pair.rs similarity index 91% rename from examples/uniswapv2.rs rename to examples/queries/examples/uniswapv2_pair.rs index 8b956112..c9d306da 100644 --- a/examples/uniswapv2.rs +++ b/examples/queries/examples/uniswapv2_pair.rs @@ -1,4 +1,8 @@ -use ethers::prelude::*; +use ethers::{ + contract::abigen, + core::types::Address, + providers::{Http, Provider}, +}; use eyre::Result; use std::sync::Arc; diff --git a/examples/subscribe_contract_events.rs b/examples/subscribe_contract_events.rs deleted file mode 100644 index a6f769f9..00000000 --- a/examples/subscribe_contract_events.rs +++ /dev/null @@ -1,38 +0,0 @@ -use ethers::prelude::*; -use eyre::Result; -use std::sync::Arc; - -// Generate the type-safe contract bindings by providing the ABI -// definition in human readable format -abigen!( - ERC20, - r#"[ - event Transfer(address indexed src, address indexed dst, uint wad) - ]"#, -); - -// In order to run this example you need to include Ws and TLS features -// Run this example with -// `cargo run -p ethers --example subscribe_contract_events --features="ws","rustls"` -#[tokio::main] -async fn main() -> Result<()> { - let client = - Provider::::connect("wss://mainnet.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27") - .await?; - - let client = Arc::new(client); - - // WETH Token - let address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2".parse::

()?; - let weth = ERC20::new(address, Arc::clone(&client)); - - // Subscribe Transfer events - let events = weth.events(); - let mut stream = events.stream().await?; - - while let Some(Ok(event)) = stream.next().await { - println!("src: {:?}, dst: {:?}, wad: {:?}", event.src, event.dst, event.wad); - } - - Ok(()) -} diff --git a/examples/subscriptions/Cargo.toml b/examples/subscriptions/Cargo.toml new file mode 100644 index 00000000..9dd76878 --- /dev/null +++ b/examples/subscriptions/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "examples-subscriptions" +version = "1.0.2" +authors = ["Andrea Simeoni "] +edition = "2021" + +[dev-dependencies] +ethers = { path = "../..", version = "1.0.0", features = ["abigen", "rustls", "ws"] } + +eyre = "0.6" +serde = { version = "1.0.144", features = ["derive"] } +serde_json = "1.0.64" +tokio = { version = "1.18", features = ["macros"] } \ No newline at end of file diff --git a/examples/subscriptions/README.md b/examples/subscriptions/README.md new file mode 100644 index 00000000..e69de29b diff --git a/examples/subscribe_events_by_type.rs b/examples/subscriptions/examples/subscribe_events_by_type.rs similarity index 82% rename from examples/subscribe_events_by_type.rs rename to examples/subscriptions/examples/subscribe_events_by_type.rs index 76e394ac..355404a3 100644 --- a/examples/subscribe_events_by_type.rs +++ b/examples/subscriptions/examples/subscribe_events_by_type.rs @@ -1,5 +1,10 @@ -use ethers::{contract::Contract, prelude::*}; +use ethers::{ + contract::{abigen, Contract}, + core::types::ValueOrArray, + providers::{Provider, StreamExt, Ws}, +}; use std::{error::Error, sync::Arc}; + abigen!( AggregatorInterface, r#"[ @@ -14,10 +19,6 @@ const PRICE_FEED_3: &str = "0xebf67ab8cff336d3f609127e8bbf8bd6dd93cd81"; /// Subscribe to a typed event stream without requiring a `Contract` instance. /// In this example we subscribe Chainlink price feeds and filter out them /// by address. -/// ------------------------------------------------------------------------------- -/// In order to run this example you need to include Ws and TLS features -/// Run this example with -/// `cargo run -p ethers --example subscribe_events_by_type --features="ws","rustls"` #[tokio::main] async fn main() -> Result<(), Box> { let client = get_client().await; diff --git a/examples/subscribe_logs.rs b/examples/subscriptions/examples/subscribe_logs.rs similarity index 82% rename from examples/subscribe_logs.rs rename to examples/subscriptions/examples/subscribe_logs.rs index b8cb1891..3ee71259 100644 --- a/examples/subscribe_logs.rs +++ b/examples/subscriptions/examples/subscribe_logs.rs @@ -1,9 +1,13 @@ -use ethers::{abi::AbiDecode, prelude::*}; +use ethers::{ + core::{ + abi::AbiDecode, + types::{Address, BlockNumber, Filter, U256}, + }, + providers::{Middleware, Provider, StreamExt, Ws}, +}; use eyre::Result; use std::sync::Arc; -// In order to run this example you need to include Ws and TLS features -// Run this example with `cargo run -p ethers --example subscribe_logs --features="ws","rustls"` #[tokio::main] async fn main() -> Result<()> { let client = diff --git a/examples/watch_blocks.rs b/examples/subscriptions/examples/watch_blocks.rs similarity index 58% rename from examples/watch_blocks.rs rename to examples/subscriptions/examples/watch_blocks.rs index af1385da..38fb90e5 100644 --- a/examples/watch_blocks.rs +++ b/examples/subscriptions/examples/watch_blocks.rs @@ -1,12 +1,13 @@ -use ethers::{prelude::*, utils::Anvil}; +use ethers::providers::{Middleware, Provider, StreamExt, Ws}; +use eyre::Result; use std::time::Duration; #[tokio::main] -async fn main() -> eyre::Result<()> { - let anvil = Anvil::new().block_time(1u64).spawn(); - let ws = Ws::connect(anvil.ws_endpoint()).await?; +async fn main() -> Result<()> { + let ws_endpoint = "wss://mainnet.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27"; + let ws = Ws::connect(ws_endpoint).await?; let provider = Provider::new(ws).interval(Duration::from_millis(2000)); - let mut stream = provider.watch_blocks().await?.take(5); + let mut stream = provider.watch_blocks().await?.take(1); while let Some(block) = stream.next().await { let block = provider.get_block(block).await?.unwrap(); println!( diff --git a/examples/transactions/Cargo.toml b/examples/transactions/Cargo.toml new file mode 100644 index 00000000..6bdf9412 --- /dev/null +++ b/examples/transactions/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "examples-transactions" +version = "1.0.2" +authors = ["Andrea Simeoni "] +edition = "2021" + +[dev-dependencies] +ethers = { path = "../..", version = "1.0.0", features = ["abigen", "rustls", "ws"] } + +eyre = "0.6" +serde = { version = "1.0.144", features = ["derive"] } +serde_json = "1.0.64" +tokio = { version = "1.18", features = ["macros"] } \ No newline at end of file diff --git a/examples/transactions/README.md b/examples/transactions/README.md new file mode 100644 index 00000000..e69de29b diff --git a/examples/call_override.rs b/examples/transactions/examples/call_override.rs similarity index 90% rename from examples/call_override.rs rename to examples/transactions/examples/call_override.rs index 2237ee1e..b80191e8 100644 --- a/examples/call_override.rs +++ b/examples/transactions/examples/call_override.rs @@ -1,14 +1,21 @@ use ethers::{ - prelude::*, - providers::call_raw::RawCall, - utils::{parse_ether, Geth}, + contract::abigen, + core::{ + types::{Address, TransactionRequest, H256}, + utils::{parse_ether, Geth}, + }, + providers::{ + call_raw::{self, RawCall}, + Http, Provider, + }, }; +use eyre::Result; use std::sync::Arc; abigen!(Greeter, "ethers-contract/tests/solidity-contracts/greeter.json",); #[tokio::main] -async fn main() -> eyre::Result<()> { +async fn main() -> Result<()> { let geth = Geth::new().spawn(); let provider = Provider::::try_from(geth.endpoint()).unwrap(); let client = Arc::new(provider); diff --git a/examples/decode_tx_input.rs b/examples/transactions/examples/decode_input.rs similarity index 94% rename from examples/decode_tx_input.rs rename to examples/transactions/examples/decode_input.rs index f11b6f1b..0eca8c37 100644 --- a/examples/decode_tx_input.rs +++ b/examples/transactions/examples/decode_input.rs @@ -1,4 +1,7 @@ -use ethers::{abi::AbiDecode, prelude::*}; +use ethers::{ + contract::abigen, + core::{abi::AbiDecode, types::Bytes}, +}; use eyre::Result; // Abigen creates a SwapExactTokensForTokensCall struct that can be used to decode diff --git a/examples/ens.rs b/examples/transactions/examples/ens.rs similarity index 87% rename from examples/ens.rs rename to examples/transactions/examples/ens.rs index 5874d5c9..0606288e 100644 --- a/examples/ens.rs +++ b/examples/transactions/examples/ens.rs @@ -1,6 +1,8 @@ -use ethers::{prelude::*, utils::Anvil}; +use ethers::{ + core::{types::TransactionRequest, utils::Anvil}, + providers::{Http, Middleware, Provider}, +}; use eyre::Result; -use std::convert::TryFrom; #[tokio::main] async fn main() -> Result<()> { diff --git a/examples/gas_price_usd.rs b/examples/transactions/examples/gas_price_usd.rs similarity index 92% rename from examples/gas_price_usd.rs rename to examples/transactions/examples/gas_price_usd.rs index 307baf82..eef46a72 100644 --- a/examples/gas_price_usd.rs +++ b/examples/transactions/examples/gas_price_usd.rs @@ -1,10 +1,18 @@ -use ethers::{prelude::*, utils::format_units}; use std::{ error::Error, ops::{Div, Mul}, sync::Arc, }; +use ethers::{ + contract::abigen, + core::{ + types::{Address, I256, U256}, + utils::format_units, + }, + providers::{Http, Middleware, Provider}, +}; + abigen!( AggregatorInterface, r#"[ diff --git a/examples/remove_liquidity.rs b/examples/transactions/examples/remove_liquidity.rs similarity index 92% rename from examples/remove_liquidity.rs rename to examples/transactions/examples/remove_liquidity.rs index 0d3e889d..587f6436 100644 --- a/examples/remove_liquidity.rs +++ b/examples/transactions/examples/remove_liquidity.rs @@ -1,6 +1,13 @@ -use ethers::prelude::*; +use std::sync::Arc; + +use ethers::{ + contract::abigen, + core::types::{Address, U256}, + middleware::SignerMiddleware, + providers::{Http, Middleware, Provider}, + signers::{LocalWallet, Signer}, +}; use eyre::Result; -use std::{convert::TryFrom, sync::Arc}; abigen!( UniswapV2Router, diff --git a/examples/geth_trace_transaction.rs b/examples/transactions/examples/trace.rs similarity index 85% rename from examples/geth_trace_transaction.rs rename to examples/transactions/examples/trace.rs index ef20f738..35ea370d 100644 --- a/examples/geth_trace_transaction.rs +++ b/examples/transactions/examples/trace.rs @@ -1,4 +1,7 @@ -use ethers::prelude::*; +use ethers::{ + core::types::{GethDebugTracingOptions, H256}, + providers::{Http, Middleware, Provider}, +}; use eyre::Result; use std::str::FromStr; diff --git a/examples/transfer_eth.rs b/examples/transactions/examples/transfer_eth.rs similarity index 90% rename from examples/transfer_eth.rs rename to examples/transactions/examples/transfer_eth.rs index 3be81cc9..00a4e6fc 100644 --- a/examples/transfer_eth.rs +++ b/examples/transactions/examples/transfer_eth.rs @@ -1,4 +1,7 @@ -use ethers::{prelude::*, utils::Anvil}; +use ethers::{ + core::{types::TransactionRequest, utils::Anvil}, + providers::{Http, Middleware, Provider}, +}; use eyre::Result; use std::convert::TryFrom; diff --git a/examples/wallets/Cargo.toml b/examples/wallets/Cargo.toml new file mode 100644 index 00000000..c3112132 --- /dev/null +++ b/examples/wallets/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "examples-wallets" +version = "1.0.2" +authors = ["Andrea Simeoni "] +edition = "2021" + +[features] +default = ["ledger", "trezor", "yubi"] +ledger = [] +trezor = [] +yubi = [] + +[dev-dependencies] +ethers = { path = "../..", version = "1.0.0", features = ["abigen", "eip712", "ledger", "rustls", "trezor", "ws", "yubi"] } + +eyre = "0.6" +serde = { version = "1.0.144", features = ["derive"] } +serde_json = "1.0.64" +tokio = { version = "1.18", features = ["macros"] } + diff --git a/examples/wallets/README.md b/examples/wallets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/examples/ledger.rs b/examples/wallets/examples/ledger.rs similarity index 100% rename from examples/ledger.rs rename to examples/wallets/examples/ledger.rs diff --git a/examples/local_signer.rs b/examples/wallets/examples/local_signer.rs similarity index 84% rename from examples/local_signer.rs rename to examples/wallets/examples/local_signer.rs index 764de4f5..b7193076 100644 --- a/examples/local_signer.rs +++ b/examples/wallets/examples/local_signer.rs @@ -1,4 +1,9 @@ -use ethers::{prelude::*, utils::Anvil}; +use ethers::{ + core::{types::TransactionRequest, utils::Anvil}, + middleware::SignerMiddleware, + providers::{Http, Middleware, Provider}, + signers::{LocalWallet, Signer}, +}; use eyre::Result; use std::convert::TryFrom; diff --git a/examples/mnemonic.rs b/examples/wallets/examples/mnemonic.rs similarity index 87% rename from examples/mnemonic.rs rename to examples/wallets/examples/mnemonic.rs index 2c14b69d..b04aaea9 100644 --- a/examples/mnemonic.rs +++ b/examples/wallets/examples/mnemonic.rs @@ -1,6 +1,10 @@ -use ethers::signers::{coins_bip39::English, MnemonicBuilder}; +use ethers::{ + core::rand, + signers::{coins_bip39::English, MnemonicBuilder}, +}; +use eyre::Result; -fn main() -> eyre::Result<()> { +fn main() -> Result<()> { let phrase = "work man father plunge mystery proud hollow address reunion sauce theory bonus"; let index = 0u32; let password = "TREZOR123"; diff --git a/examples/permit_hash.rs b/examples/wallets/examples/permit_hash.rs similarity index 89% rename from examples/permit_hash.rs rename to examples/wallets/examples/permit_hash.rs index b313b3a5..ac28588c 100644 --- a/examples/permit_hash.rs +++ b/examples/wallets/examples/permit_hash.rs @@ -1,7 +1,9 @@ use ethers::{ contract::{Eip712, EthAbiType}, - core::types::transaction::eip712::Eip712, - types::{Address, U256}, + core::{ + types::{transaction::eip712::Eip712, Address, U256}, + utils::hex, + }, }; // Generate the EIP712 permit hash to sign for a Uniswap V2 pair. diff --git a/examples/sign.rs b/examples/wallets/examples/sign_message.rs similarity index 91% rename from examples/sign.rs rename to examples/wallets/examples/sign_message.rs index 6f6b592e..07d50d87 100644 --- a/examples/sign.rs +++ b/examples/wallets/examples/sign_message.rs @@ -1,9 +1,9 @@ // use the eyre crate for easy idiomatic error handling use eyre::Result; // use the ethers_core rand for rng -use ethers_core::rand::thread_rng; +use ethers::core::rand::thread_rng; // use the ethers_signers crate to manage LocalWallet and Signer -use ethers_signers::{LocalWallet, Signer}; +use ethers::signers::{LocalWallet, Signer}; // Use the `tokio::main` macro for using async on the main function #[tokio::main] diff --git a/examples/trezor.rs b/examples/wallets/examples/trezor.rs similarity index 100% rename from examples/trezor.rs rename to examples/wallets/examples/trezor.rs diff --git a/examples/yubi.rs b/examples/wallets/examples/yubi.rs similarity index 100% rename from examples/yubi.rs rename to examples/wallets/examples/yubi.rs diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..c438805c --- /dev/null +++ b/flake.lock @@ -0,0 +1,271 @@ +{ + "nodes": { + "devenv": { + "inputs": { + "flake-compat": "flake-compat", + "nix": "nix", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1671704144, + "narHash": "sha256-k+jPOOTeRFt1zzgb8gU+lnedbZs8dzscnlA0c29VFcQ=", + "owner": "cachix", + "repo": "devenv", + "rev": "01fd16d1fd24ec2ace19ce1f3f3ce90038d9d042", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1671690212, + "narHash": "sha256-iLWnd/lyCIULpsJcFcrpA5JilMff7vzoniEcWLggp7o=", + "owner": "nix-community", + "repo": "fenix", + "rev": "645713f090f2e40ff6b072a10b1d890e5ee6ea23", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1668681692, + "narHash": "sha256-Ht91NGdewz8IQLtWZ9LCeNXMSXHUss+9COoqu6JLmXU=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "009399224d5e398d03b22badca40a37ac85412a1", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "lowdown-src": { + "flake": false, + "locked": { + "lastModified": 1633514407, + "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", + "owner": "kristapsdz", + "repo": "lowdown", + "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "type": "github" + }, + "original": { + "owner": "kristapsdz", + "repo": "lowdown", + "type": "github" + } + }, + "nix": { + "inputs": { + "lowdown-src": "lowdown-src", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1671638174, + "narHash": "sha256-FeEmVix8l/HglWtRgeHOfjqEm2etvp+MLYd1C/raq3Y=", + "owner": "domenkozar", + "repo": "nix", + "rev": "51b770e985f9e1b84fb5e03a983ef1e19f18c3e9", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "relaxed-flakes", + "repo": "nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1671458120, + "narHash": "sha256-2+k/OONN4OF21TeoNjKB5sXVZv6Zvm/uEyQIW9OYCg8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e37ef84b478fa8da0ced96522adfd956fde9047a", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1671271954, + "narHash": "sha256-cSvu+bnvN08sOlTBWbBrKaBHQZq8mvk8bgpt0ZJ2Snc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d513b448cc2a6da2c8803e3c197c9fc7e67b19e3", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-22.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1671458120, + "narHash": "sha256-2+k/OONN4OF21TeoNjKB5sXVZv6Zvm/uEyQIW9OYCg8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e37ef84b478fa8da0ced96522adfd956fde9047a", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "flake-utils": "flake-utils", + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1671452357, + "narHash": "sha256-HqzXiQEegpRQ4VEl9pEPgHSIxhJrNJ27HfN1wOc7w2E=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "200790e9c77064c53eaf95805b013d96615ecc27", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "fenix": "fenix", + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs_2" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1671659018, + "narHash": "sha256-y3btK8GxfZG599y0Y6yfMjPs4jz72Eej9FuWUMPrB7A=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "271f7b44d3d71db2ec2d0cd1a8a59aa16f902d49", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..64a21c7b --- /dev/null +++ b/flake.nix @@ -0,0 +1,58 @@ +{ + inputs = { + devenv.url = "github:cachix/devenv"; + flake-utils.url = "github:numtide/flake-utils"; + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { self + , nixpkgs + , flake-utils + , fenix + , devenv + } @ inputs: + flake-utils.lib.eachDefaultSystem (system: + let pkgs = import nixpkgs { + inherit system; + }; + in + { + devShell = devenv.lib.mkShell { + inherit inputs pkgs; + + modules = [ + { + packages = with pkgs; [ + solc + ] ++ lib.optionals stdenv.isDarwin (with darwin.apple_sdk; [ + libiconv + frameworks.Security + ]); + + # https://devenv.sh/languages/ + languages.nix.enable = true; + languages.rust = { + enable = true; + version = "stable"; + packages = { + rustfmt = inputs.fenix.packages.${pkgs.system}.latest.rustfmt; + clippy = inputs.fenix.packages.${pkgs.system}.latest.clippy; + }; + }; + + # https://devenv.sh/pre-commit-hooks/ + pre-commit.hooks = { + shellcheck.enable = true; + + clippy.enable = true; + rustfmt.enable = true; + }; + } + ]; + }; + }); +} diff --git a/scripts/examples.sh b/scripts/examples.sh index ee50628e..90537870 100755 --- a/scripts/examples.sh +++ b/scripts/examples.sh @@ -1,25 +1,42 @@ -set -e +#!/bin/bash + +set -e; # Fail fast # shellcheck shell=bash # examples that we can't run because they require some additional infra, docker or ledger for example ignored=( - "moonbeam_with_abi" - "ipc" - "ledger" - "paginated_logs" - "subscribe_logs" - "trezor" - "yubi" - "remove_liquidity" + "examples-contracts:deploy_moonbeam" + "examples-providers:ipc" + "examples-wallets:ledger" + "examples-wallets:trezor" + "examples-wallets:yubi" + "examples-transactions:remove_liquidity" ) -# run all examples -for file in examples/*.rs; do - name="$(echo "$file" | cut -f 1 -d '.')" - if [[ "${ignored[*]}" =~ $(basename "$name") ]]; then - echo "skipping: $file" - continue - fi - echo "running: $file" - cargo r -p ethers --example "$(basename "$name")" --features "ethers-solc rustls ws" +example_crates=$(cargo metadata --format-version 1 | + jq -c '.workspace_members' | + jq -r 'map(select(startswith("examples")) | + sub("\\s.*$";"")) | .[]') + +for crate in $example_crates; do + # Remove "examples-" prefix from crate name (e.g. examples-contracts => contracts) + cratedir="${crate#examples-}" + srcdir="examples/$cratedir/examples" + # Retrieve all example files in crate: + # Transform the absolute path into the filename (e.g. some-path/deploy_anvil.rs => deploy_anvil) + example_files=$(find $srcdir -type f -name '*.rs' -exec basename {} \; | sed 's/\.[^.]*$//') + + for file in $example_files; do + # Build the example + echo "Building example: $crate:$file" + cargo build -p $crate --example $file + + # Run the example + if [[ "${ignored[*]}" =~ $(basename "$crate:$file") ]]; then + echo "skipping: $crate:$file" + continue + fi + echo "running $crate:$file" + cargo run -p $crate --example $file + done done