WIP add contract abi
This commit is contained in:
parent
548b24e518
commit
1dd3c3dc89
|
@ -189,6 +189,20 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ethabi"
|
||||||
|
version = "12.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "052a565e3de82944527d6d10a465697e6bb92476b772ca7141080c901f6a63c6"
|
||||||
|
dependencies = [
|
||||||
|
"ethereum-types",
|
||||||
|
"rustc-hex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tiny-keccak 1.5.0",
|
||||||
|
"uint",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethbloom"
|
name = "ethbloom"
|
||||||
version = "0.9.2"
|
version = "0.9.2"
|
||||||
|
@ -199,7 +213,7 @@ dependencies = [
|
||||||
"fixed-hash",
|
"fixed-hash",
|
||||||
"impl-rlp",
|
"impl-rlp",
|
||||||
"impl-serde",
|
"impl-serde",
|
||||||
"tiny-keccak",
|
"tiny-keccak 2.0.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -221,6 +235,7 @@ name = "ethers"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"ethabi",
|
||||||
"ethereum-types",
|
"ethereum-types",
|
||||||
"failure",
|
"failure",
|
||||||
"rand 0.5.6",
|
"rand 0.5.6",
|
||||||
|
@ -232,7 +247,7 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"solc",
|
"solc",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tiny-keccak",
|
"tiny-keccak 2.0.2",
|
||||||
"tokio",
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
|
@ -1226,6 +1241,15 @@ dependencies = [
|
||||||
"winapi 0.3.8",
|
"winapi 0.3.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiny-keccak"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2"
|
||||||
|
dependencies = [
|
||||||
|
"crunchy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiny-keccak"
|
name = "tiny-keccak"
|
||||||
version = "2.0.2"
|
version = "2.0.2"
|
||||||
|
|
|
@ -20,6 +20,7 @@ tiny-keccak = { version = "2.0.2", default-features = false }
|
||||||
|
|
||||||
solc = { git = "https://github.com/paritytech/rust_solc "}
|
solc = { git = "https://github.com/paritytech/rust_solc "}
|
||||||
rlp = "0.4.5"
|
rlp = "0.4.5"
|
||||||
|
ethabi = "12.0.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { version = "0.2.21", features = ["macros"] }
|
tokio = { version = "0.2.21", features = ["macros"] }
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
use ethers::{
|
||||||
|
types::{Address, Filter},
|
||||||
|
Contract, HttpProvider, MainnetWallet,
|
||||||
|
};
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), failure::Error> {
|
||||||
|
// connect to the network
|
||||||
|
let provider = HttpProvider::try_from("http://localhost:8545")?;
|
||||||
|
|
||||||
|
// create a wallet and connect it to the provider
|
||||||
|
let client = "15c42bf2987d5a8a73804a8ea72fb4149f88adf73e98fc3f8a8ce9f24fcb7774"
|
||||||
|
.parse::<MainnetWallet>()?
|
||||||
|
.connect(&provider);
|
||||||
|
|
||||||
|
// Contract should take both provider or a signer
|
||||||
|
|
||||||
|
let contract = Contract::new(
|
||||||
|
"f817796F60D268A36a57b8D2dF1B97B14C0D0E1d".parse::<Address>()?,
|
||||||
|
abi,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
//! This module implements extensions to the `ethabi` API.
|
||||||
|
//! Taken from: https://github.com/gnosis/ethcontract-rs/blob/master/common/src/abiext.rs
|
||||||
|
|
||||||
|
use ethabi::{Event, Function, ParamType};
|
||||||
|
use crate::{utils::id, types::Selector};
|
||||||
|
|
||||||
|
/// Extension trait for `ethabi::Function`.
|
||||||
|
pub trait FunctionExt {
|
||||||
|
/// Compute the method signature in the standard ABI format. This does not
|
||||||
|
/// include the output types.
|
||||||
|
fn abi_signature(&self) -> String;
|
||||||
|
|
||||||
|
/// Compute the Keccak256 function selector used by contract ABIs.
|
||||||
|
fn selector(&self) -> Selector;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FunctionExt for Function {
|
||||||
|
fn abi_signature(&self) -> String {
|
||||||
|
let mut full_signature = self.signature();
|
||||||
|
if let Some(colon) = full_signature.find(':') {
|
||||||
|
full_signature.truncate(colon);
|
||||||
|
}
|
||||||
|
|
||||||
|
full_signature
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selector(&self) -> Selector {
|
||||||
|
id(self.abi_signature())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extension trait for `ethabi::Event`.
|
||||||
|
pub trait EventExt {
|
||||||
|
/// Compute the event signature in human-readable format. The `keccak256`
|
||||||
|
/// hash of this value is the actual event signature that is used as topic0
|
||||||
|
/// in the transaction logs.
|
||||||
|
fn abi_signature(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventExt for Event {
|
||||||
|
fn abi_signature(&self) -> String {
|
||||||
|
format!(
|
||||||
|
"{}({}){}",
|
||||||
|
self.name,
|
||||||
|
self.inputs
|
||||||
|
.iter()
|
||||||
|
.map(|input| input.kind.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(","),
|
||||||
|
if self.anonymous { " anonymous" } else { "" },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn format_function_signature() {
|
||||||
|
for (f, expected) in &[
|
||||||
|
(r#"{"name":"foo","inputs":[],"outputs":[]}"#, "foo()"),
|
||||||
|
(
|
||||||
|
r#"{"name":"bar","inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"bool"}],"outputs":[]}"#,
|
||||||
|
"bar(uint256,bool)",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r#"{"name":"baz","inputs":[{"name":"a","type":"uint256"}],"outputs":[{"name":"b","type":"bool"}]}"#,
|
||||||
|
"baz(uint256)",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r#"{"name":"bax","inputs":[],"outputs":[{"name":"a","type":"uint256"},{"name":"b","type":"bool"}]}"#,
|
||||||
|
"bax()",
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
let function: Function = serde_json::from_str(f).expect("invalid function JSON");
|
||||||
|
let signature = function.abi_signature();
|
||||||
|
assert_eq!(signature, *expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn format_event_signature() {
|
||||||
|
for (e, expected) in &[
|
||||||
|
(r#"{"name":"foo","inputs":[],"anonymous":false}"#, "foo()"),
|
||||||
|
(
|
||||||
|
r#"{"name":"bar","inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"bool"}],"anonymous":false}"#,
|
||||||
|
"bar(uint256,bool)",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r#"{"name":"baz","inputs":[{"name":"a","type":"uint256"}],"anonymous":true}"#,
|
||||||
|
"baz(uint256) anonymous",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r#"{"name":"bax","inputs":[],"anonymous":true}"#,
|
||||||
|
"bax() anonymous",
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
let event: Event = serde_json::from_str(e).expect("invalid event JSON");
|
||||||
|
let signature = event.abi_signature();
|
||||||
|
assert_eq!(signature, *expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
use crate::types::Address;
|
||||||
|
|
||||||
|
mod abi;
|
||||||
|
|
||||||
|
pub struct Contract<ABI> {
|
||||||
|
pub address: Address,
|
||||||
|
pub abi: ABI,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<ABI> Contract<ABI> {
|
||||||
|
pub fn new<A: Into<Address>>(address: A, abi: ABI) -> Self {
|
||||||
|
Self {
|
||||||
|
address: address.into(),
|
||||||
|
abi,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,8 +18,10 @@
|
||||||
pub mod providers;
|
pub mod providers;
|
||||||
pub use providers::HttpProvider;
|
pub use providers::HttpProvider;
|
||||||
|
|
||||||
pub mod signers;
|
mod contract;
|
||||||
|
pub use contract::Contract;
|
||||||
|
|
||||||
|
mod signers;
|
||||||
pub use signers::{AnyWallet, MainnetWallet, Signer};
|
pub use signers::{AnyWallet, MainnetWallet, Signer};
|
||||||
|
|
||||||
/// Ethereum related datatypes
|
/// Ethereum related datatypes
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
//! Various Ethereum Related Datatypes
|
//! Various Ethereum Related Datatypes
|
||||||
|
|
||||||
|
pub type Selector = [u8; 4];
|
||||||
|
|
||||||
// Re-export common ethereum datatypes with more specific names
|
// Re-export common ethereum datatypes with more specific names
|
||||||
pub use ethereum_types::H256 as TxHash;
|
pub use ethereum_types::H256 as TxHash;
|
||||||
pub use ethereum_types::{Address, Bloom, H256, U256, U64};
|
pub use ethereum_types::{Address, Bloom, H256, U256, U64};
|
||||||
|
@ -21,3 +23,5 @@ pub use block::{Block, BlockId, BlockNumber};
|
||||||
|
|
||||||
mod log;
|
mod log;
|
||||||
pub use log::{Filter, Log};
|
pub use log::{Filter, Log};
|
||||||
|
|
||||||
|
|
||||||
|
|
22
src/utils.rs
22
src/utils.rs
|
@ -1,5 +1,5 @@
|
||||||
//! Various utilities for manipulating Ethereum related dat
|
//! Various utilities for manipulating Ethereum related dat
|
||||||
use crate::types::H256;
|
use crate::types::{H256, Selector};
|
||||||
use tiny_keccak::{Hasher, Keccak};
|
use tiny_keccak::{Hasher, Keccak};
|
||||||
|
|
||||||
const PREFIX: &str = "\x19Ethereum Signed Message:\n";
|
const PREFIX: &str = "\x19Ethereum Signed Message:\n";
|
||||||
|
@ -31,12 +31,14 @@ pub fn keccak256(bytes: &[u8]) -> [u8; 32] {
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the first 4 bytes
|
/// Calculate the function selector as per the contract ABI specification. This
|
||||||
pub fn id(name: &str) -> [u8; 4] {
|
/// is defined as the first 4 bytes of the Keccak256 hash of the function
|
||||||
|
/// signature.
|
||||||
|
pub fn id<S: AsRef<str>>(signature: S) -> Selector {
|
||||||
let mut output = [0u8; 4];
|
let mut output = [0u8; 4];
|
||||||
|
|
||||||
let mut hasher = Keccak::v256();
|
let mut hasher = Keccak::v256();
|
||||||
hasher.update(name.as_bytes());
|
hasher.update(signature.as_ref().as_bytes());
|
||||||
hasher.finalize(&mut output);
|
hasher.finalize(&mut output);
|
||||||
|
|
||||||
output
|
output
|
||||||
|
@ -64,4 +66,16 @@ mod tests {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_function_signature() {
|
||||||
|
// test vector retrieved from
|
||||||
|
// https://web3js.readthedocs.io/en/v1.2.4/web3-eth-abi.html#encodefunctionsignature
|
||||||
|
assert_eq!(id("myMethod(uint256,string)"), [0x24, 0xee, 0x00, 0x97],);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn revert_function_signature() {
|
||||||
|
assert_eq!(id("Error(string)"), [0x08, 0xc3, 0x79, 0xa0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue