feat(signers): implement Serde and make Wallet API smaller (#20)
* feat(signers): implement Serde and make API smaller * fix: add abigen as a dev-dependency feature
This commit is contained in:
parent
570b45eb10
commit
1a47e933ae
|
@ -377,6 +377,7 @@ dependencies = [
|
||||||
"ethers-core",
|
"ethers-core",
|
||||||
"ethers-providers",
|
"ethers-providers",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
@ -1163,18 +1164,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.111"
|
version = "1.0.112"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d"
|
checksum = "736aac72d1eafe8e5962d1d1c3d99b0df526015ba40915cb3c49d042e92ec243"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.111"
|
version = "1.0.112"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250"
|
checksum = "bf0343ce212ac0d3d6afd9391ac8e9c9efe06b533c8d33f660f6390cc4093f57"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
@ -14,8 +14,8 @@ ethers-core = { version = "0.1.0", path = "../ethers-core" }
|
||||||
|
|
||||||
serde = { version = "1.0.110", default-features = false }
|
serde = { version = "1.0.110", default-features = false }
|
||||||
rustc-hex = { version = "2.1.0", default-features = false }
|
rustc-hex = { version = "2.1.0", default-features = false }
|
||||||
thiserror = { version = "1.0.19", default-features = false }
|
thiserror = { version = "1.0.15", default-features = false }
|
||||||
once_cell = { version = "1.4.0", default-features = false }
|
once_cell = { version = "1.3.1", default-features = false }
|
||||||
futures = "0.3.5"
|
futures = "0.3.5"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -23,5 +23,4 @@ tokio = { version = "0.2.21", default-features = false, features = ["macros"] }
|
||||||
serde_json = "1.0.55"
|
serde_json = "1.0.55"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["abigen"]
|
|
||||||
abigen = ["ethers-contract-abigen", "ethers-contract-derive"]
|
abigen = ["ethers-contract-abigen", "ethers-contract-derive"]
|
||||||
|
|
|
@ -17,5 +17,5 @@ quote = "1.0"
|
||||||
syn = "1.0.12"
|
syn = "1.0.12"
|
||||||
url = "2.1"
|
url = "2.1"
|
||||||
serde_json = "1.0.53"
|
serde_json = "1.0.53"
|
||||||
once_cell = "1.4.0"
|
once_cell = "1.3.1"
|
||||||
rustc-hex = { version = "2.1.0", default-features = false }
|
rustc-hex = { version = "2.1.0", default-features = false }
|
||||||
|
|
|
@ -6,6 +6,8 @@ use quote::quote;
|
||||||
|
|
||||||
pub(crate) fn imports() -> TokenStream {
|
pub(crate) fn imports() -> TokenStream {
|
||||||
quote! {
|
quote! {
|
||||||
|
#![allow(dead_code)]
|
||||||
|
#![allow(unused_imports)]
|
||||||
// TODO: Can we make this context aware so that it imports either ethers_contract
|
// TODO: Can we make this context aware so that it imports either ethers_contract
|
||||||
// or ethers::contract?
|
// or ethers::contract?
|
||||||
use ethers::{
|
use ethers::{
|
||||||
|
|
|
@ -40,6 +40,7 @@ pub enum ContractError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
#[must_use = "contract calls do nothing unless you `send` or `call` them"]
|
||||||
/// Helper for managing a transaction before submitting it to a node
|
/// Helper for managing a transaction before submitting it to a node
|
||||||
pub struct ContractCall<'a, P, S, D> {
|
pub struct ContractCall<'a, P, S, D> {
|
||||||
/// The raw transaction object
|
/// The raw transaction object
|
||||||
|
|
|
@ -11,6 +11,7 @@ use futures::stream::{Stream, StreamExt};
|
||||||
use std::{collections::HashMap, marker::PhantomData};
|
use std::{collections::HashMap, marker::PhantomData};
|
||||||
|
|
||||||
/// Helper for managing the event filter before querying or streaming its logs
|
/// Helper for managing the event filter before querying or streaming its logs
|
||||||
|
#[must_use = "event filters do nothing unless you `query` or `stream` them"]
|
||||||
pub struct Event<'a: 'b, 'b, P, D> {
|
pub struct Event<'a: 'b, 'b, P, D> {
|
||||||
/// The event filter's state
|
/// The event filter's state
|
||||||
pub filter: Filter,
|
pub filter: Filter,
|
||||||
|
|
|
@ -20,7 +20,7 @@ tiny-keccak = { version = "2.0.2", default-features = false }
|
||||||
serde = { version = "1.0.110", default-features = false, features = ["derive"] }
|
serde = { version = "1.0.110", default-features = false, features = ["derive"] }
|
||||||
serde_json = { version = "1.0.53", default-features = false, features = ["alloc"] }
|
serde_json = { version = "1.0.53", default-features = false, features = ["alloc"] }
|
||||||
rustc-hex = { version = "2.1.0", default-features = false }
|
rustc-hex = { version = "2.1.0", default-features = false }
|
||||||
thiserror = { version = "1.0.19", default-features = false }
|
thiserror = { version = "1.0.15", default-features = false }
|
||||||
arrayvec = { version = "0.5.1", default-features = false, optional = true }
|
arrayvec = { version = "0.5.1", default-features = false, optional = true }
|
||||||
glob = "0.3.0"
|
glob = "0.3.0"
|
||||||
|
|
||||||
|
|
|
@ -6,16 +6,48 @@ use crate::{
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rustc_hex::FromHex;
|
use rustc_hex::FromHex;
|
||||||
use secp256k1::{
|
use secp256k1::{
|
||||||
self as Secp256k1, Error as SecpError, Message, PublicKey as PubKey, RecoveryId, SecretKey,
|
self as Secp256k1,
|
||||||
|
util::{COMPRESSED_PUBLIC_KEY_SIZE, SECRET_KEY_SIZE},
|
||||||
|
Error as SecpError, Message, PublicKey as PubKey, RecoveryId, SecretKey,
|
||||||
};
|
};
|
||||||
use std::ops::Deref;
|
use serde::{
|
||||||
use std::str::FromStr;
|
de::Error as DeserializeError,
|
||||||
|
de::{SeqAccess, Visitor},
|
||||||
|
ser::SerializeTuple,
|
||||||
|
Deserialize, Deserializer, Serialize, Serializer,
|
||||||
|
};
|
||||||
|
use std::{fmt, ops::Deref, str::FromStr};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
/// A private key on Secp256k1
|
/// A private key on Secp256k1
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct PrivateKey(pub(super) SecretKey);
|
pub struct PrivateKey(pub(super) SecretKey);
|
||||||
|
|
||||||
|
impl Serialize for PrivateKey {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let mut seq = serializer.serialize_tuple(SECRET_KEY_SIZE)?;
|
||||||
|
for e in &self.0.serialize() {
|
||||||
|
seq.serialize_element(e)?;
|
||||||
|
}
|
||||||
|
seq.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for PrivateKey {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let bytes = <[u8; SECRET_KEY_SIZE]>::deserialize(deserializer)?;
|
||||||
|
Ok(PrivateKey(
|
||||||
|
SecretKey::parse(&bytes).map_err(DeserializeError::custom)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromStr for PrivateKey {
|
impl FromStr for PrivateKey {
|
||||||
type Err = SecpError;
|
type Err = SecpError;
|
||||||
|
|
||||||
|
@ -133,7 +165,6 @@ impl PrivateKey {
|
||||||
let r = H256::from_slice(&signature.r.b32());
|
let r = H256::from_slice(&signature.r.b32());
|
||||||
let s = H256::from_slice(&signature.s.b32());
|
let s = H256::from_slice(&signature.s.b32());
|
||||||
|
|
||||||
// TODO: Check what happens when using the 1337 Geth chain id
|
|
||||||
Signature { v: v as u8, r, s }
|
Signature { v: v as u8, r, s }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,14 +245,80 @@ impl From<PrivateKey> for Address {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Serialize for PublicKey {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let mut seq = serializer.serialize_tuple(COMPRESSED_PUBLIC_KEY_SIZE)?;
|
||||||
|
for e in self.0.serialize_compressed().iter() {
|
||||||
|
seq.serialize_element(e)?;
|
||||||
|
}
|
||||||
|
seq.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for PublicKey {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct ArrayVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for ArrayVisitor {
|
||||||
|
type Value = PublicKey;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("a valid proof")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_seq<S>(self, mut seq: S) -> Result<PublicKey, S::Error>
|
||||||
|
where
|
||||||
|
S: SeqAccess<'de>,
|
||||||
|
{
|
||||||
|
let mut bytes = [0u8; COMPRESSED_PUBLIC_KEY_SIZE];
|
||||||
|
for b in &mut bytes[..] {
|
||||||
|
*b = seq
|
||||||
|
.next_element()?
|
||||||
|
.ok_or_else(|| DeserializeError::custom("could not read bytes"))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(PublicKey(
|
||||||
|
PubKey::parse_compressed(&bytes).map_err(DeserializeError::custom)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_tuple(COMPRESSED_PUBLIC_KEY_SIZE, ArrayVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::types::Bytes;
|
|
||||||
use rustc_hex::FromHex;
|
use rustc_hex::FromHex;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serde() {
|
||||||
|
for _ in 0..10 {
|
||||||
|
let key = PrivateKey::new(&mut rand::thread_rng());
|
||||||
|
let serialized = bincode::serialize(&key).unwrap();
|
||||||
|
assert_eq!(serialized, &key.0.serialize());
|
||||||
|
let de: PrivateKey = bincode::deserialize(&serialized).unwrap();
|
||||||
|
assert_eq!(key, de);
|
||||||
|
|
||||||
|
let public = PublicKey::from(&key);
|
||||||
|
let serialized = bincode::serialize(&public).unwrap();
|
||||||
|
assert_eq!(&serialized[..], public.0.serialize_compressed().as_ref());
|
||||||
|
let de: PublicKey = bincode::deserialize(&serialized).unwrap();
|
||||||
|
assert_eq!(public, de);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn signs_tx() {
|
fn signs_tx() {
|
||||||
|
use crate::types::{Address, Bytes};
|
||||||
|
|
||||||
// retrieved test vector from:
|
// retrieved test vector from:
|
||||||
// https://web3js.readthedocs.io/en/v1.2.0/web3-eth-accounts.html#eth-accounts-signtransaction
|
// https://web3js.readthedocs.io/en/v1.2.0/web3-eth-accounts.html#eth-accounts-signtransaction
|
||||||
let tx = TransactionRequest {
|
let tx = TransactionRequest {
|
||||||
|
|
|
@ -11,7 +11,7 @@ async-trait = { version = "0.1.31", default-features = false }
|
||||||
reqwest = { version = "0.10.4", default-features = false, features = ["json", "rustls-tls"] }
|
reqwest = { version = "0.10.4", default-features = false, features = ["json", "rustls-tls"] }
|
||||||
serde = { version = "1.0.110", default-features = false, features = ["derive"] }
|
serde = { version = "1.0.110", default-features = false, features = ["derive"] }
|
||||||
serde_json = { version = "1.0.53", default-features = false }
|
serde_json = { version = "1.0.53", default-features = false }
|
||||||
thiserror = { version = "1.0.19", default-features = false }
|
thiserror = { version = "1.0.15", default-features = false }
|
||||||
url = { version = "2.1.1", default-features = false }
|
url = { version = "2.1.1", default-features = false }
|
||||||
|
|
||||||
# required for implementing stream on the filters
|
# required for implementing stream on the filters
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub use provider::{Provider, ProviderError};
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
/// Trait which must be implemented by data transports to be used with the Ethereum
|
/// Trait which must be implemented by data transports to be used with the Ethereum
|
||||||
/// JSON-RPC provider.
|
/// JSON-RPC provider.
|
||||||
pub trait JsonRpcClient: Debug + Clone {
|
pub trait JsonRpcClient: Debug + Clone + Send + Sync {
|
||||||
/// A JSON-RPC Error
|
/// A JSON-RPC Error
|
||||||
type Error: Error + Into<ProviderError>;
|
type Error: Error + Into<ProviderError>;
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,9 @@ edition = "2018"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ethers-core = { version = "0.1.0", path = "../ethers-core" }
|
ethers-core = { version = "0.1.0", path = "../ethers-core" }
|
||||||
ethers-providers = { version = "0.1.0", path = "../ethers-providers" }
|
ethers-providers = { version = "0.1.0", path = "../ethers-providers" }
|
||||||
thiserror = { version = "1.0.19", default-features = false }
|
thiserror = { version = "1.0.15", default-features = false }
|
||||||
futures-util = { version = "0.3.5", default-features = false }
|
futures-util = { version = "0.3.5", default-features = false }
|
||||||
|
serde = "1.0.112"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { version = "0.2.21", features = ["macros"] }
|
tokio = { version = "0.2.21", features = ["macros"] }
|
||||||
|
|
|
@ -12,7 +12,7 @@ use std::error::Error;
|
||||||
///
|
///
|
||||||
/// Implement this trait to support different signing modes, e.g. Ledger, hosted etc.
|
/// Implement this trait to support different signing modes, e.g. Ledger, hosted etc.
|
||||||
// TODO: We might need a `SignerAsync` trait for HSM use cases?
|
// TODO: We might need a `SignerAsync` trait for HSM use cases?
|
||||||
pub trait Signer: Clone {
|
pub trait Signer: Clone + Send + Sync {
|
||||||
type Error: Error + Into<ClientError>;
|
type Error: Error + Into<ClientError>;
|
||||||
/// Signs the hash of the provided message after prefixing it
|
/// Signs the hash of the provided message after prefixing it
|
||||||
fn sign_message<S: AsRef<[u8]>>(&self, message: S) -> Signature;
|
fn sign_message<S: AsRef<[u8]>>(&self, message: S) -> Signature;
|
||||||
|
|
|
@ -8,6 +8,7 @@ use ethers_core::{
|
||||||
types::{Address, PrivateKey, PublicKey, Signature, Transaction, TransactionRequest, TxError},
|
types::{Address, PrivateKey, PublicKey, Signature, Transaction, TransactionRequest, TxError},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
/// An Ethereum private-public key pair which can be used for signing messages. It can be connected to a provider
|
/// An Ethereum private-public key pair which can be used for signing messages. It can be connected to a provider
|
||||||
|
@ -29,7 +30,7 @@ use std::str::FromStr;
|
||||||
///
|
///
|
||||||
/// // Optionally, the wallet's chain id can be set, in order to use EIP-155
|
/// // Optionally, the wallet's chain id can be set, in order to use EIP-155
|
||||||
/// // replay protection with different chains
|
/// // replay protection with different chains
|
||||||
/// let wallet = wallet.chain_id(1337u64);
|
/// let wallet = wallet.set_chain_id(1337u64);
|
||||||
///
|
///
|
||||||
/// // The wallet can be used to sign messages
|
/// // The wallet can be used to sign messages
|
||||||
/// let message = b"hello";
|
/// let message = b"hello";
|
||||||
|
@ -62,16 +63,16 @@ use std::str::FromStr;
|
||||||
/// [`connect`]: ./struct.Wallet.html#method.connect
|
/// [`connect`]: ./struct.Wallet.html#method.connect
|
||||||
/// [`Signature`]: ../ethers_core/types/struct.Signature.html
|
/// [`Signature`]: ../ethers_core/types/struct.Signature.html
|
||||||
/// [`hash_message`]: ../ethers_core/utils/fn.hash_message.html
|
/// [`hash_message`]: ../ethers_core/utils/fn.hash_message.html
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Wallet {
|
pub struct Wallet {
|
||||||
/// The Wallet's private Key
|
/// The Wallet's private Key
|
||||||
pub private_key: PrivateKey,
|
private_key: PrivateKey,
|
||||||
/// The Wallet's public Key
|
/// The Wallet's public Key
|
||||||
pub public_key: PublicKey,
|
public_key: PublicKey,
|
||||||
/// The wallet's address
|
/// The wallet's address
|
||||||
pub address: Address,
|
address: Address,
|
||||||
/// The wallet's chain id (for EIP-155), signs w/o replay protection if left unset
|
/// The wallet's chain id (for EIP-155), signs w/o replay protection if left unset
|
||||||
pub chain_id: u64,
|
chain_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Signer for Wallet {
|
impl Signer for Wallet {
|
||||||
|
@ -123,11 +124,26 @@ impl Wallet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the wallet's chain_id
|
/// Sets the wallet's chain_id, used in conjunction with EIP-155 signing
|
||||||
pub fn chain_id<T: Into<u64>>(mut self, chain_id: T) -> Self {
|
pub fn set_chain_id<T: Into<u64>>(mut self, chain_id: T) -> Self {
|
||||||
self.chain_id = chain_id.into();
|
self.chain_id = chain_id.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the wallet's public key
|
||||||
|
pub fn public_key(&self) -> &PublicKey {
|
||||||
|
&self.public_key
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the wallet's private key
|
||||||
|
pub fn private_key(&self) -> &PrivateKey {
|
||||||
|
&self.private_key
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the wallet's chain id
|
||||||
|
pub fn chain_id(&self) -> u64 {
|
||||||
|
self.chain_id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PrivateKey> for Wallet {
|
impl From<PrivateKey> for Wallet {
|
||||||
|
|
|
@ -19,6 +19,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||||
features = ["full"]
|
features = ["full"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
abigen = ["contract", "ethers-contract/abigen"]
|
||||||
default = ["full"]
|
default = ["full"]
|
||||||
full = [
|
full = [
|
||||||
"contract",
|
"contract",
|
||||||
|
@ -27,20 +28,22 @@ full = [
|
||||||
"core",
|
"core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
core = ["ethers-core"]
|
||||||
contract = ["ethers-contract"]
|
contract = ["ethers-contract"]
|
||||||
providers = ["ethers-providers"]
|
providers = ["ethers-providers"]
|
||||||
signers = ["ethers-signers"]
|
signers = ["ethers-signers"]
|
||||||
core = ["ethers-core"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ethers-contract = { version = "0.1.0", path = "../ethers-contract", features = ["abigen"], optional = true }
|
ethers-contract = { version = "0.1.0", path = "../ethers-contract", optional = true }
|
||||||
|
ethers-core = { version = "0.1.0", path = "../ethers-core", optional = true }
|
||||||
ethers-providers = { version = "0.1.0", path = "../ethers-providers", optional = true }
|
ethers-providers = { version = "0.1.0", path = "../ethers-providers", optional = true }
|
||||||
ethers-signers = { version = "0.1.0", path = "../ethers-signers", optional = true }
|
ethers-signers = { version = "0.1.0", path = "../ethers-signers", optional = true }
|
||||||
ethers-core = { version = "0.1.0", path = "../ethers-core", optional = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
ethers-contract = { version = "0.1.0", path = "../ethers-contract", features = ["abigen"] }
|
||||||
|
|
||||||
anyhow = "1.0.31"
|
anyhow = "1.0.31"
|
||||||
tokio = { version = "0.2.21", features = ["macros"] }
|
|
||||||
serde_json = "1.0.53"
|
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
serde = { version = "1.0.110", features = ["derive"] }
|
serde = { version = "1.0.110", features = ["derive"] }
|
||||||
|
serde_json = "1.0.53"
|
||||||
|
tokio = { version = "0.2.21", features = ["macros"] }
|
||||||
|
|
|
@ -11,7 +11,7 @@ fn main() {
|
||||||
// recover the address that signed it
|
// recover the address that signed it
|
||||||
let recovered = signature.recover(message).unwrap();
|
let recovered = signature.recover(message).unwrap();
|
||||||
|
|
||||||
assert_eq!(recovered, wallet.address);
|
assert_eq!(recovered, wallet.address());
|
||||||
|
|
||||||
println!("Verified signature produced by {:?}!", wallet.address);
|
println!("Verified signature produced by {:?}!", wallet.address());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue