fix(abigen): correctly parse params in human readable abi (#194)
* fix(abigen): correctly parse params in human readable abi * chore: make clippy happy * test: remove unwrap * chore: make clippy happy again * chore: fix contract.rs example * chore: rename to contract using human readable format * examples: add abigen example with path to abi * fix: pin funty version to fix bitvec error * chore: remove unused import * chore: fix deps
This commit is contained in:
parent
457c646511
commit
a43299c838
|
@ -681,6 +681,7 @@ dependencies = [
|
||||||
"ethabi-next",
|
"ethabi-next",
|
||||||
"ethereum-types",
|
"ethereum-types",
|
||||||
"ethers",
|
"ethers",
|
||||||
|
"funty",
|
||||||
"generic-array",
|
"generic-array",
|
||||||
"glob",
|
"glob",
|
||||||
"hex",
|
"hex",
|
||||||
|
|
|
@ -107,7 +107,7 @@ impl Context {
|
||||||
// heuristic for parsing the human readable format
|
// heuristic for parsing the human readable format
|
||||||
|
|
||||||
// replace bad chars
|
// replace bad chars
|
||||||
let abi_str = abi_str.replace('[', "").replace(']', "").replace(',', "");
|
let abi_str = abi_str.replace('[', "").replace(']', "");
|
||||||
// split lines and get only the non-empty things
|
// split lines and get only the non-empty things
|
||||||
let split: Vec<&str> = abi_str
|
let split: Vec<&str> = abi_str
|
||||||
.split('\n')
|
.split('\n')
|
||||||
|
|
|
@ -36,7 +36,7 @@ pub(crate) fn struct_declaration(cx: &Context, abi_name: &proc_macro2::Ident) ->
|
||||||
} else {
|
} else {
|
||||||
quote! {
|
quote! {
|
||||||
pub static #abi_name: Lazy<Abi> = Lazy::new(|| {
|
pub static #abi_name: Lazy<Abi> = Lazy::new(|| {
|
||||||
let abi_str = #abi.replace('[', "").replace(']', "").replace(',', "");
|
let abi_str = #abi.replace('[', "").replace(']', "");
|
||||||
// split lines and get only the non-empty things
|
// split lines and get only the non-empty things
|
||||||
let split: Vec<&str> = abi_str
|
let split: Vec<&str> = abi_str
|
||||||
.split("\n")
|
.split("\n")
|
||||||
|
|
|
@ -30,7 +30,7 @@ impl Context {
|
||||||
.abi
|
.abi
|
||||||
.events()
|
.events()
|
||||||
.map(|event| expand_filter(event))
|
.map(|event| expand_filter(event))
|
||||||
.collect::<Result<Vec<_>>>()?;
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if data_types.is_empty() {
|
if data_types.is_empty() {
|
||||||
return Ok(quote! {});
|
return Ok(quote! {});
|
||||||
|
@ -43,7 +43,7 @@ impl Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expands into a single method for contracting an event stream.
|
/// Expands into a single method for contracting an event stream.
|
||||||
fn expand_filter(event: &Event) -> Result<TokenStream> {
|
fn expand_filter(event: &Event) -> TokenStream {
|
||||||
// append `filter` to disambiguate with potentially conflicting
|
// append `filter` to disambiguate with potentially conflicting
|
||||||
// function names
|
// function names
|
||||||
let name = util::safe_ident(&format!("{}_filter", event.name.to_snake_case()));
|
let name = util::safe_ident(&format!("{}_filter", event.name.to_snake_case()));
|
||||||
|
@ -53,13 +53,13 @@ fn expand_filter(event: &Event) -> Result<TokenStream> {
|
||||||
let ev_name = Literal::string(&event.name);
|
let ev_name = Literal::string(&event.name);
|
||||||
|
|
||||||
let doc = util::expand_doc(&format!("Gets the contract's `{}` event", event.name));
|
let doc = util::expand_doc(&format!("Gets the contract's `{}` event", event.name));
|
||||||
Ok(quote! {
|
quote! {
|
||||||
|
|
||||||
#doc
|
#doc
|
||||||
pub fn #name(&self) -> Event<M, #result> {
|
pub fn #name(&self) -> Event<M, #result> {
|
||||||
self.0.event(#ev_name).expect("event not found (this should never happen)")
|
self.0.event(#ev_name).expect("event not found (this should never happen)")
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expands an ABI event into a single event data type. This can expand either
|
/// Expands an ABI event into a single event data type. This can expand either
|
||||||
|
@ -317,7 +317,7 @@ mod tests {
|
||||||
anonymous: false,
|
anonymous: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_quote!(expand_filter(&event).unwrap(), {
|
assert_quote!(expand_filter(&event), {
|
||||||
#[doc = "Gets the contract's `Transfer` event"]
|
#[doc = "Gets the contract's `Transfer` event"]
|
||||||
pub fn transfer_filter(&self) -> Event<M, TransferFilter> {
|
pub fn transfer_filter(&self) -> Event<M, TransferFilter> {
|
||||||
self.0
|
self.0
|
||||||
|
|
|
@ -32,12 +32,17 @@ glob = { version = "0.3.0", default-features = false }
|
||||||
bytes = { version = "1.0.1", features = ["serde"] }
|
bytes = { version = "1.0.1", features = ["serde"] }
|
||||||
hex = { version = "0.4.2", default-features = false, features = ["std"] }
|
hex = { version = "0.4.2", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
|
# bitvec compilation issue
|
||||||
|
# https://github.com/bitvecto-rs/bitvec/issues/105#issuecomment-778570981
|
||||||
|
funty = "=1.1.0"
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ethers = { version = "0.2", path = "../ethers" }
|
ethers = { version = "0.2", path = "../ethers" }
|
||||||
|
|
||||||
serde_json = { version = "1.0.62", default-features = false }
|
serde_json = { version = "1.0.62", default-features = false }
|
||||||
bincode = { version = "1.2.1", default-features = false }
|
bincode = { version = "1.2.1", default-features = false }
|
||||||
once_cell = { version = "1.5.2", default-features = false }
|
once_cell = { version = "1.5.2" }
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
|
@ -248,15 +248,13 @@ where
|
||||||
mut tx: TransactionRequest,
|
mut tx: TransactionRequest,
|
||||||
block: Option<BlockNumber>,
|
block: Option<BlockNumber>,
|
||||||
) -> Result<PendingTransaction<'_, Self::Provider>, Self::Error> {
|
) -> Result<PendingTransaction<'_, Self::Provider>, Self::Error> {
|
||||||
if let Some(ref to) = tx.to {
|
if let Some(NameOrAddress::Name(ens_name)) = tx.to {
|
||||||
if let NameOrAddress::Name(ens_name) = to {
|
let addr = self
|
||||||
let addr = self
|
.inner
|
||||||
.inner
|
.resolve_name(&ens_name)
|
||||||
.resolve_name(&ens_name)
|
.await
|
||||||
.await
|
.map_err(SignerMiddlewareError::MiddlewareError)?;
|
||||||
.map_err(SignerMiddlewareError::MiddlewareError)?;
|
tx.to = Some(addr.into())
|
||||||
tx.to = Some(addr.into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill any missing fields
|
// fill any missing fields
|
||||||
|
|
|
@ -59,15 +59,13 @@ where
|
||||||
block: Option<BlockNumber>,
|
block: Option<BlockNumber>,
|
||||||
) -> Result<PendingTransaction<'_, Self::Provider>, Self::Error> {
|
) -> Result<PendingTransaction<'_, Self::Provider>, Self::Error> {
|
||||||
// resolve the to field if that's an ENS name.
|
// resolve the to field if that's an ENS name.
|
||||||
if let Some(ref to) = tx.to {
|
if let Some(NameOrAddress::Name(ens_name)) = tx.to {
|
||||||
if let NameOrAddress::Name(ens_name) = to {
|
let addr = self
|
||||||
let addr = self
|
.inner
|
||||||
.inner
|
.resolve_name(&ens_name)
|
||||||
.resolve_name(&ens_name)
|
.await
|
||||||
.await
|
.map_err(TransformerMiddlewareError::MiddlewareError)?;
|
||||||
.map_err(TransformerMiddlewareError::MiddlewareError)?;
|
tx.to = Some(addr.into())
|
||||||
tx.to = Some(addr.into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// construct the appropriate proxy tx.
|
// construct the appropriate proxy tx.
|
||||||
|
|
|
@ -73,12 +73,8 @@ impl<'a, P: JsonRpcClient> Future for PendingTransaction<'a, P> {
|
||||||
ctx.waker().wake_by_ref();
|
ctx.waker().wake_by_ref();
|
||||||
}
|
}
|
||||||
PendingTxState::GettingReceipt(fut) => {
|
PendingTxState::GettingReceipt(fut) => {
|
||||||
if let Ok(receipt) = futures_util::ready!(fut.as_mut().poll(ctx)) {
|
if let Ok(Some(receipt)) = futures_util::ready!(fut.as_mut().poll(ctx)) {
|
||||||
if let Some(receipt) = receipt {
|
*this.state = PendingTxState::CheckingReceipt(Box::new(receipt))
|
||||||
*this.state = PendingTxState::CheckingReceipt(Box::new(receipt))
|
|
||||||
} else {
|
|
||||||
*this.state = PendingTxState::PausedGettingReceipt
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
*this.state = PendingTxState::PausedGettingReceipt
|
*this.state = PendingTxState::PausedGettingReceipt
|
||||||
}
|
}
|
||||||
|
|
|
@ -293,14 +293,12 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
|
||||||
tx.gas = Some(self.estimate_gas(&tx).await?);
|
tx.gas = Some(self.estimate_gas(&tx).await?);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref to) = tx.to {
|
if let Some(NameOrAddress::Name(ref ens_name)) = tx.to {
|
||||||
if let NameOrAddress::Name(ens_name) = to {
|
// resolve to an address
|
||||||
// resolve to an address
|
let addr = self.resolve_name(&ens_name).await?;
|
||||||
let addr = self.resolve_name(&ens_name).await?;
|
|
||||||
|
|
||||||
// set the value
|
// set the value
|
||||||
tx.to = Some(addr.into())
|
tx.to = Some(addr.into())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let tx_hash = self.request("eth_sendTransaction", [tx]).await?;
|
let tx_hash = self.request("eth_sendTransaction", [tx]).await?;
|
||||||
|
|
|
@ -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"}]
|
|
@ -12,7 +12,7 @@ abigen!(
|
||||||
r#"[
|
r#"[
|
||||||
function setValue(string)
|
function setValue(string)
|
||||||
function getValue() external view (string)
|
function getValue() external view (string)
|
||||||
event ValueChanged(address indexed author, address indexed oldAuthor, string oldValue, string newValue)
|
event ValueChanged(address indexed author, string oldValue, string newValue)
|
||||||
]"#,
|
]"#,
|
||||||
event_derives(serde::Deserialize, serde::Serialize)
|
event_derives(serde::Deserialize, serde::Serialize)
|
||||||
);
|
);
|
||||||
|
@ -20,7 +20,7 @@ abigen!(
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
// 1. compile the contract (note this requires that you are inside the `ethers/examples` directory)
|
// 1. compile the contract (note this requires that you are inside the `ethers/examples` directory)
|
||||||
let compiled = Solc::new("./contract.sol").build()?;
|
let compiled = Solc::new("**/contract.sol").build()?;
|
||||||
let contract = compiled
|
let contract = compiled
|
||||||
.get("SimpleStorage")
|
.get("SimpleStorage")
|
||||||
.expect("could not find contract");
|
.expect("could not find contract");
|
|
@ -0,0 +1,72 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use ethers::{
|
||||||
|
prelude::*,
|
||||||
|
utils::{Ganache, Solc},
|
||||||
|
};
|
||||||
|
use std::{convert::TryFrom, sync::Arc, time::Duration};
|
||||||
|
|
||||||
|
// Generate the type-safe contract bindings by providing the ABI
|
||||||
|
// definition in human readable format
|
||||||
|
abigen!(
|
||||||
|
SimpleContract,
|
||||||
|
"./ethers/examples/contract_abi.json",
|
||||||
|
event_derives(serde::Deserialize, serde::Serialize)
|
||||||
|
);
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
// 1. compile the contract (note this requires that you are inside the `ethers/examples` directory)
|
||||||
|
let compiled = Solc::new("**/contract.sol").build()?;
|
||||||
|
let contract = compiled
|
||||||
|
.get("SimpleStorage")
|
||||||
|
.expect("could not find contract");
|
||||||
|
dbg!("OK");
|
||||||
|
|
||||||
|
// 2. launch ganache
|
||||||
|
let ganache = Ganache::new().spawn();
|
||||||
|
|
||||||
|
// 3. instantiate our wallet
|
||||||
|
let wallet: LocalWallet = ganache.keys()[0].clone().into();
|
||||||
|
|
||||||
|
// 4. connect to the network
|
||||||
|
let provider =
|
||||||
|
Provider::<Http>::try_from(ganache.endpoint())?.interval(Duration::from_millis(10u64));
|
||||||
|
|
||||||
|
// 5. instantiate the client with the wallet
|
||||||
|
let client = SignerMiddleware::new(provider, wallet);
|
||||||
|
let client = Arc::new(client);
|
||||||
|
|
||||||
|
// 6. create a factory which will be used to deploy instances of the contract
|
||||||
|
let factory = ContractFactory::new(
|
||||||
|
contract.abi.clone(),
|
||||||
|
contract.bytecode.clone(),
|
||||||
|
client.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 7. deploy it with the constructor arguments
|
||||||
|
let contract = factory.deploy("initial value".to_string())?.send().await?;
|
||||||
|
|
||||||
|
// 8. get the contract's address
|
||||||
|
let addr = contract.address();
|
||||||
|
|
||||||
|
// 9. instantiate the contract
|
||||||
|
let contract = SimpleContract::new(addr, client.clone());
|
||||||
|
|
||||||
|
// 10. 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()).send().await?.await?;
|
||||||
|
|
||||||
|
// 11. get all events
|
||||||
|
let logs = contract
|
||||||
|
.value_changed_filter()
|
||||||
|
.from_block(0u64)
|
||||||
|
.query()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// 12. get the new value
|
||||||
|
let value = contract.get_value().call().await?;
|
||||||
|
|
||||||
|
println!("Value: {}. Logs: {}", value, serde_json::to_string(&logs)?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue