diff --git a/ethers-core/src/types/chain.rs b/ethers-core/src/types/chain.rs index 28ad23c3..7dd6fefb 100644 --- a/ethers-core/src/types/chain.rs +++ b/ethers-core/src/types/chain.rs @@ -2,7 +2,7 @@ use std::fmt; use crate::types::U256; -#[derive(Debug)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum Chain { Mainnet, Ropsten, @@ -15,6 +15,9 @@ pub enum Chain { Avalanche, AvalancheFuji, Sepolia, + Moonbeam, + MoonbeamDev, + Moonriver, } impl fmt::Display for Chain { @@ -37,6 +40,9 @@ impl From for u32 { Chain::Avalanche => 43114, Chain::AvalancheFuji => 43113, Chain::Sepolia => 11155111, + Chain::Moonbeam => 1287, + Chain::MoonbeamDev => 1281, + Chain::Moonriver => 1285, } } } @@ -46,3 +52,9 @@ impl From for U256 { u32::from(chain).into() } } + +impl From for u64 { + fn from(chain: Chain) -> Self { + u32::from(chain).into() + } +} diff --git a/ethers-core/src/utils/mod.rs b/ethers-core/src/utils/mod.rs index 7b16f1a5..722d0e83 100644 --- a/ethers-core/src/utils/mod.rs +++ b/ethers-core/src/utils/mod.rs @@ -10,6 +10,9 @@ mod geth; #[cfg(not(target_arch = "wasm32"))] pub use geth::{Geth, GethInstance}; +/// Moonbeam utils +pub mod moonbeam; + mod hash; pub use hash::{hash_message, id, keccak256, serialize}; diff --git a/ethers-core/src/utils/moonbeam.rs b/ethers-core/src/utils/moonbeam.rs new file mode 100644 index 00000000..d054d8e7 --- /dev/null +++ b/ethers-core/src/utils/moonbeam.rs @@ -0,0 +1,146 @@ +//! Moonbeam utilities + +use std::collections::BTreeMap; + +use k256::SecretKey; + +/// Returns the private developer keys https://docs.moonbeam.network/snippets/code/setting-up-node/dev-accounts/ +pub fn dev_keys() -> Vec { + MoonbeamDev::default().into_keys().collect() +} + +/// Holds private developer keys with their names +#[derive(Debug, Clone)] +pub struct MoonbeamDev { + keys: BTreeMap<&'static str, SecretKey>, +} + +impl MoonbeamDev { + pub fn keys(&self) -> impl Iterator { + self.keys.values() + } + + pub fn into_keys(self) -> impl Iterator { + self.keys.into_values() + } + + /// Get a key by then, like `Alith` + pub fn get(&self, name: impl AsRef) -> Option<&SecretKey> { + self.keys.get(name.as_ref()) + } + + pub fn alith(&self) -> &SecretKey { + self.get("Alith").unwrap() + } + + pub fn baltathar(&self) -> &SecretKey { + self.get("Baltathar").unwrap() + } + + pub fn charleth(&self) -> &SecretKey { + self.get("Charleth").unwrap() + } + + pub fn ethan(&self) -> &SecretKey { + self.get("Ethan").unwrap() + } +} + +impl Default for MoonbeamDev { + fn default() -> Self { + Self { + keys: BTreeMap::from([ + ( + "Alith", + SecretKey::from_bytes( + hex::decode( + "5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133", + ) + .unwrap(), + ) + .unwrap(), + ), + ( + "Baltathar", + SecretKey::from_bytes( + hex::decode( + "8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b", + ) + .unwrap(), + ) + .unwrap(), + ), + ( + "Charleth", + SecretKey::from_bytes( + hex::decode( + "0b6e18cafb6ed99687ec547bd28139cafdd2bffe70e6b688025de6b445aa5c5b", + ) + .unwrap(), + ) + .unwrap(), + ), + ( + "Dorothy", + SecretKey::from_bytes( + hex::decode( + "39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68", + ) + .unwrap(), + ) + .unwrap(), + ), + ( + "Faith", + SecretKey::from_bytes( + hex::decode( + "b9d2ea9a615f3165812e8d44de0d24da9bbd164b65c4f0573e1ce2c8dbd9c8df", + ) + .unwrap(), + ) + .unwrap(), + ), + ( + "Goliath", + SecretKey::from_bytes( + hex::decode( + "96b8a38e12e1a31dee1eab2fffdf9d9990045f5b37e44d8cc27766ef294acf18", + ) + .unwrap(), + ) + .unwrap(), + ), + ( + "Heath", + SecretKey::from_bytes( + hex::decode( + "0d6dcaaef49272a5411896be8ad16c01c35d6f8c18873387b71fbc734759b0ab", + ) + .unwrap(), + ) + .unwrap(), + ), + ( + "Ida", + SecretKey::from_bytes( + hex::decode( + "4c42532034540267bf568198ccec4cb822a025da542861fcb146a5fab6433ff8", + ) + .unwrap(), + ) + .unwrap(), + ), + ( + "Judith", + SecretKey::from_bytes( + hex::decode( + "94c49300a58d576011096bcb006aa06f5a91b34b4383891e8029c21dc39fbb8b", + ) + .unwrap(), + ) + .unwrap(), + ), + ]), + } + } +} diff --git a/ethers-etherscan/src/lib.rs b/ethers-etherscan/src/lib.rs index 400bcf82..772e64ca 100644 --- a/ethers-etherscan/src/lib.rs +++ b/ethers-etherscan/src/lib.rs @@ -78,7 +78,11 @@ impl Client { Chain::Mainnet | Chain::Ropsten | Chain::Kovan | Chain::Rinkeby | Chain::Goerli => { std::env::var("ETHERSCAN_API_KEY")? } + Chain::XDai | Chain::Sepolia => String::default(), + Chain::Moonbeam | Chain::MoonbeamDev | Chain::Moonriver => { + std::env::var("MOONSCAN_API_KEY")? + } }; Self::new(chain, api_key) } diff --git a/examples/moonbeam_with_abi.rs b/examples/moonbeam_with_abi.rs new file mode 100644 index 00000000..c455d5f3 --- /dev/null +++ b/examples/moonbeam_with_abi.rs @@ -0,0 +1,75 @@ +use anyhow::Result; +use ethers::prelude::*; +use std::{convert::TryFrom, path::Path, sync::Arc, time::Duration}; + +abigen!( + SimpleContract, + "./examples/contract_abi.json", + event_derives(serde::Deserialize, serde::Serialize) +); + +const MOONBEAM_DEV_ENDPOINT: &str = "http://localhost:9933"; + +/// This requires a running moonbeam dev instance on `localhost:9933` +/// See `https://docs.moonbeam.network/builders/get-started/moonbeam-dev/` for reference +/// +/// This has been tested against: +/// +/// ```bash +/// docker run --rm --name moonbeam_development -p 9944:9944 -p 9933:9933 purestake/moonbeam:v0.14.2 --dev --ws-external --rpc-external +/// ``` +/// +/// Also requires the `legacy` feature to send Legacy transaction instead of an EIP-1559 +#[tokio::main] +#[cfg(feature = "legacy")] +async fn main() -> Result<()> { + // 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"); + 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(); + + // 1. get a moonbeam dev key + let key = ethers::core::utils::moonbeam::dev_keys()[0].clone(); + + // 2. instantiate our wallet with chain id + let wallet: LocalWallet = LocalWallet::from(key).with_chain_id(Chain::MoonbeamDev); + + // 3. connect to the network + let provider = + Provider::::try_from(MOONBEAM_DEV_ENDPOINT)?.interval(Duration::from_millis(10u64)); + + // 4. instantiate the client with the wallet + let client = SignerMiddleware::new(provider, wallet); + let client = Arc::new(client); + + // 5. create a factory which will be used to deploy instances of the contract + let factory = ContractFactory::new(abi, bytecode, client.clone()); + + // 6. deploy it with the constructor arguments, note the `legacy` call + let contract = factory.deploy("initial value".to_string())?.legacy().send().await?; + + // 7. get the contract's address + let addr = contract.address(); + + // 8. instantiate the contract + let contract = SimpleContract::new(addr, client.clone()); + + // 9. call the `setValue` method + // (first `await` returns a PendingTransaction, second one waits for it to be mined) + let _receipt = contract.set_value("hi".to_owned()).legacy().send().await?.await?; + + // 10. get all events + let logs = contract.value_changed_filter().from_block(0u64).query().await?; + + // 11. get the new value + let value = contract.get_value().call().await?; + + println!("Value: {}. Logs: {}", value, serde_json::to_string(&logs)?); + + Ok(()) +} + +#[cfg(not(feature = "legacy"))] +fn main() {}