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",
|
||||
"ethereum-types",
|
||||
"ethers",
|
||||
"funty",
|
||||
"generic-array",
|
||||
"glob",
|
||||
"hex",
|
||||
|
|
|
@ -107,7 +107,7 @@ impl Context {
|
|||
// heuristic for parsing the human readable format
|
||||
|
||||
// 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
|
||||
let split: Vec<&str> = abi_str
|
||||
.split('\n')
|
||||
|
|
|
@ -36,7 +36,7 @@ pub(crate) fn struct_declaration(cx: &Context, abi_name: &proc_macro2::Ident) ->
|
|||
} else {
|
||||
quote! {
|
||||
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
|
||||
let split: Vec<&str> = abi_str
|
||||
.split("\n")
|
||||
|
|
|
@ -30,7 +30,7 @@ impl Context {
|
|||
.abi
|
||||
.events()
|
||||
.map(|event| expand_filter(event))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if data_types.is_empty() {
|
||||
return Ok(quote! {});
|
||||
|
@ -43,7 +43,7 @@ impl Context {
|
|||
}
|
||||
|
||||
/// 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
|
||||
// function names
|
||||
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 doc = util::expand_doc(&format!("Gets the contract's `{}` event", event.name));
|
||||
Ok(quote! {
|
||||
quote! {
|
||||
|
||||
#doc
|
||||
pub fn #name(&self) -> Event<M, #result> {
|
||||
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
|
||||
|
@ -317,7 +317,7 @@ mod tests {
|
|||
anonymous: false,
|
||||
};
|
||||
|
||||
assert_quote!(expand_filter(&event).unwrap(), {
|
||||
assert_quote!(expand_filter(&event), {
|
||||
#[doc = "Gets the contract's `Transfer` event"]
|
||||
pub fn transfer_filter(&self) -> Event<M, TransferFilter> {
|
||||
self.0
|
||||
|
|
|
@ -32,12 +32,17 @@ glob = { version = "0.3.0", default-features = false }
|
|||
bytes = { version = "1.0.1", features = ["serde"] }
|
||||
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]
|
||||
ethers = { version = "0.2", path = "../ethers" }
|
||||
|
||||
serde_json = { version = "1.0.62", 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]
|
||||
|
|
|
@ -248,15 +248,13 @@ where
|
|||
mut tx: TransactionRequest,
|
||||
block: Option<BlockNumber>,
|
||||
) -> Result<PendingTransaction<'_, Self::Provider>, Self::Error> {
|
||||
if let Some(ref to) = tx.to {
|
||||
if let NameOrAddress::Name(ens_name) = to {
|
||||
let addr = self
|
||||
.inner
|
||||
.resolve_name(&ens_name)
|
||||
.await
|
||||
.map_err(SignerMiddlewareError::MiddlewareError)?;
|
||||
tx.to = Some(addr.into())
|
||||
}
|
||||
if let Some(NameOrAddress::Name(ens_name)) = tx.to {
|
||||
let addr = self
|
||||
.inner
|
||||
.resolve_name(&ens_name)
|
||||
.await
|
||||
.map_err(SignerMiddlewareError::MiddlewareError)?;
|
||||
tx.to = Some(addr.into())
|
||||
}
|
||||
|
||||
// fill any missing fields
|
||||
|
|
|
@ -59,15 +59,13 @@ where
|
|||
block: Option<BlockNumber>,
|
||||
) -> Result<PendingTransaction<'_, Self::Provider>, Self::Error> {
|
||||
// resolve the to field if that's an ENS name.
|
||||
if let Some(ref to) = tx.to {
|
||||
if let NameOrAddress::Name(ens_name) = to {
|
||||
let addr = self
|
||||
.inner
|
||||
.resolve_name(&ens_name)
|
||||
.await
|
||||
.map_err(TransformerMiddlewareError::MiddlewareError)?;
|
||||
tx.to = Some(addr.into())
|
||||
}
|
||||
if let Some(NameOrAddress::Name(ens_name)) = tx.to {
|
||||
let addr = self
|
||||
.inner
|
||||
.resolve_name(&ens_name)
|
||||
.await
|
||||
.map_err(TransformerMiddlewareError::MiddlewareError)?;
|
||||
tx.to = Some(addr.into())
|
||||
}
|
||||
|
||||
// construct the appropriate proxy tx.
|
||||
|
|
|
@ -73,12 +73,8 @@ impl<'a, P: JsonRpcClient> Future for PendingTransaction<'a, P> {
|
|||
ctx.waker().wake_by_ref();
|
||||
}
|
||||
PendingTxState::GettingReceipt(fut) => {
|
||||
if let Ok(receipt) = futures_util::ready!(fut.as_mut().poll(ctx)) {
|
||||
if let Some(receipt) = receipt {
|
||||
*this.state = PendingTxState::CheckingReceipt(Box::new(receipt))
|
||||
} else {
|
||||
*this.state = PendingTxState::PausedGettingReceipt
|
||||
}
|
||||
if let Ok(Some(receipt)) = futures_util::ready!(fut.as_mut().poll(ctx)) {
|
||||
*this.state = PendingTxState::CheckingReceipt(Box::new(receipt))
|
||||
} else {
|
||||
*this.state = PendingTxState::PausedGettingReceipt
|
||||
}
|
||||
|
|
|
@ -293,14 +293,12 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
|
|||
tx.gas = Some(self.estimate_gas(&tx).await?);
|
||||
}
|
||||
|
||||
if let Some(ref to) = tx.to {
|
||||
if let NameOrAddress::Name(ens_name) = to {
|
||||
// resolve to an address
|
||||
let addr = self.resolve_name(&ens_name).await?;
|
||||
if let Some(NameOrAddress::Name(ref ens_name)) = tx.to {
|
||||
// resolve to an address
|
||||
let addr = self.resolve_name(&ens_name).await?;
|
||||
|
||||
// set the value
|
||||
tx.to = Some(addr.into())
|
||||
}
|
||||
// set the value
|
||||
tx.to = Some(addr.into())
|
||||
}
|
||||
|
||||
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#"[
|
||||
function setValue(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)
|
||||
);
|
||||
|
@ -20,7 +20,7 @@ abigen!(
|
|||
#[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 compiled = Solc::new("**/contract.sol").build()?;
|
||||
let contract = compiled
|
||||
.get("SimpleStorage")
|
||||
.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