feat: Adds support for launching ganache-cli
This commit is contained in:
parent
2bba40a788
commit
7a17b2dec2
|
@ -31,7 +31,7 @@ impl<'a, 'b, P, N, D: Detokenize> Event<'a, 'b, P, N, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn topic0<T: Into<ValueOrArray<H256>>>(mut self, topic: T) -> Self {
|
pub fn topic0<T: Into<ValueOrArray<H256>>>(mut self, topic: T) -> Self {
|
||||||
self.filter.topics[0] = topic.into();
|
self.filter.topics[0] = Some(topic.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
use std::process::{Child, Command};
|
||||||
|
|
||||||
|
/// A ganache CLI instance. Will close the instance when dropped.
|
||||||
|
pub struct Ganache(Child);
|
||||||
|
|
||||||
|
impl Drop for Ganache {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.0.kill().expect("could not kill ganache");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builder for launching `ganache-cli`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If `spawn` is called without `ganache-cli` being available in the user's $PATH
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// use ethers_utils::ganache::GanacheBuilder;
|
||||||
|
/// let port = 8545u64;
|
||||||
|
/// let url = format!("http://localhost:{}", port).to_string();
|
||||||
|
///
|
||||||
|
/// let ganache = GanacheBuilder::new()
|
||||||
|
/// .port(port)
|
||||||
|
/// .mnemonic("abstract vacuum mammal awkward pudding scene penalty purchase dinner depart evoke puzzle")
|
||||||
|
/// .spawn();
|
||||||
|
///
|
||||||
|
/// drop(ganache); // this will kill the instance
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct GanacheBuilder {
|
||||||
|
port: Option<u64>,
|
||||||
|
mnemonic: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GanacheBuilder {
|
||||||
|
/// Creates an empty Ganache builder.
|
||||||
|
/// The default port is 8545. The mnemonic is chosen randomly.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the port which will be used when the `ganache-cli` instance is launched.
|
||||||
|
pub fn port<T: Into<u64>>(mut self, port: T) -> Self {
|
||||||
|
self.port = Some(port.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the mnemonic which will be used when the `ganache-cli` instance is launched.
|
||||||
|
pub fn mnemonic<T: Into<String>>(mut self, mnemonic: T) -> Self {
|
||||||
|
self.mnemonic = Some(mnemonic.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the builder and spawns `ganache-cli` with stdout redirected
|
||||||
|
/// to /dev/null. This takes ~2 seconds to execute as it blocks while
|
||||||
|
/// waiting for `ganache-cli` to launch.
|
||||||
|
pub fn spawn(self) -> Ganache {
|
||||||
|
let mut cmd = Command::new("ganache-cli");
|
||||||
|
cmd.stdout(std::process::Stdio::null());
|
||||||
|
if let Some(port) = self.port {
|
||||||
|
cmd.arg("-p").arg(port.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mnemonic) = self.mnemonic {
|
||||||
|
cmd.arg("-m").arg(mnemonic);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ganache_pid = cmd.spawn().expect("couldnt start ganache-cli");
|
||||||
|
|
||||||
|
// wait a couple of seconds for ganache to boot up
|
||||||
|
// TODO: Change this to poll for `port`
|
||||||
|
let sleep_time = std::time::Duration::from_secs(2);
|
||||||
|
std::thread::sleep(sleep_time);
|
||||||
|
Ganache(ganache_pid)
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,9 @@
|
||||||
use ethereum_types::H256;
|
use ethereum_types::H256;
|
||||||
use tiny_keccak::{Hasher, Keccak};
|
use tiny_keccak::{Hasher, Keccak};
|
||||||
|
|
||||||
|
/// Utilities for launching a ganache-cli testnet instance
|
||||||
|
pub mod ganache;
|
||||||
|
|
||||||
const PREFIX: &str = "\x19Ethereum Signed Message:\n";
|
const PREFIX: &str = "\x19Ethereum Signed Message:\n";
|
||||||
|
|
||||||
/// Hash a message according to EIP-191.
|
/// Hash a message according to EIP-191.
|
||||||
|
|
|
@ -13,7 +13,7 @@ async fn main() -> Result<()> {
|
||||||
let filter = Filter::new()
|
let filter = Filter::new()
|
||||||
.address_str("f817796F60D268A36a57b8D2dF1B97B14C0D0E1d")?
|
.address_str("f817796F60D268A36a57b8D2dF1B97B14C0D0E1d")?
|
||||||
.event("ValueChanged(address,string,string)") // event name
|
.event("ValueChanged(address,string,string)") // event name
|
||||||
.topic("9729a6fbefefc8f6005933898b13dc45c3a2c8b7".parse::<Address>()?); // indexed param
|
.topic0("9729a6fbefefc8f6005933898b13dc45c3a2c8b7".parse::<Address>()?); // indexed param
|
||||||
|
|
||||||
let logs = provider.get_logs(&filter).await?;
|
let logs = provider.get_logs(&filter).await?;
|
||||||
println!("Got logs: {}", serde_json::to_string(&logs).unwrap());
|
println!("Got logs: {}", serde_json::to_string(&logs).unwrap());
|
||||||
|
|
|
@ -1,16 +1,27 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use ethers::{providers::HttpProvider, signers::MainnetWallet, types::TransactionRequest};
|
use ethers::{
|
||||||
|
providers::HttpProvider, signers::MainnetWallet, types::TransactionRequest,
|
||||||
|
utils::ganache::GanacheBuilder,
|
||||||
|
};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
// connect to the network
|
let port = 8545u64;
|
||||||
let provider = HttpProvider::try_from("http://localhost:8545")?;
|
let url = format!("http://localhost:{}", port).to_string();
|
||||||
|
let _ganache = GanacheBuilder::new()
|
||||||
|
.port(port)
|
||||||
|
.mnemonic("abstract vacuum mammal awkward pudding scene penalty purchase dinner depart evoke puzzle")
|
||||||
|
.spawn();
|
||||||
|
// this private key belongs to the above mnemonic
|
||||||
|
let wallet: MainnetWallet =
|
||||||
|
"380eb0f3d505f087e438eca80bc4df9a7faa24f868e69fc0440261a0fc0567dc".parse()?;
|
||||||
|
|
||||||
// create a wallet and connect it to the provider
|
// connect to the network
|
||||||
let client = "dcf2cbdd171a21c480aa7f53d77f31bb102282b3ff099c78e3118b37348c72f7"
|
let provider = HttpProvider::try_from(url.as_str())?;
|
||||||
.parse::<MainnetWallet>()?
|
|
||||||
.connect(&provider);
|
// connect the wallet to the provider
|
||||||
|
let client = wallet.connect(&provider);
|
||||||
|
|
||||||
// craft the transaction
|
// craft the transaction
|
||||||
let tx = TransactionRequest::new()
|
let tx = TransactionRequest::new()
|
||||||
|
@ -25,8 +36,8 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
let receipt = client.get_transaction_receipt(tx.hash).await?;
|
let receipt = client.get_transaction_receipt(tx.hash).await?;
|
||||||
|
|
||||||
println!("{}", serde_json::to_string(&tx)?);
|
println!("Send tx: {}", serde_json::to_string(&tx)?);
|
||||||
println!("{}", serde_json::to_string(&receipt)?);
|
println!("Tx receipt: {}", serde_json::to_string(&receipt)?);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,18 @@ use anyhow::Result;
|
||||||
use ethers::{
|
use ethers::{
|
||||||
providers::{networks::Any, HttpProvider},
|
providers::{networks::Any, HttpProvider},
|
||||||
types::{BlockNumber, TransactionRequest},
|
types::{BlockNumber, TransactionRequest},
|
||||||
|
utils::ganache::GanacheBuilder,
|
||||||
};
|
};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
|
let port = 8546u64;
|
||||||
|
let url = format!("http://localhost:{}", port).to_string();
|
||||||
|
let _ganache = GanacheBuilder::new().port(port).spawn();
|
||||||
|
|
||||||
// connect to the network
|
// connect to the network
|
||||||
let provider = HttpProvider::<Any>::try_from("http://localhost:8545")?;
|
let provider = HttpProvider::<Any>::try_from(url.as_str())?;
|
||||||
let accounts = provider.get_accounts().await?;
|
let accounts = provider.get_accounts().await?;
|
||||||
let from = accounts[0];
|
let from = accounts[0];
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue