ethers-rs/ethers-contract/tests/it/contract.rs

964 lines
37 KiB
Rust
Raw Normal View History

#![allow(unused)]
pub use crate::common::*;
use ethers_contract::{abigen, ContractFactory, EthAbiType};
use ethers_core::types::{Filter, ValueOrArray, H256};
2020-06-01 23:15:33 +00:00
#[cfg(not(feature = "celo"))]
mod eth_tests {
use super::*;
use ethers_contract::{ContractInstance, EthEvent, LogMeta, Multicall, MulticallVersion};
use ethers_core::{
abi::{encode, Detokenize, Token, Tokenizable},
types::{transaction::eip712::Eip712, Address, BlockId, Bytes, H160, I256, U256},
utils::{keccak256, Anvil},
};
derive-eip712: initial implementation of eip712 derive macro (#481) * derive-eip712: initial implementation of eip712 derive macro This commit provides an initial implementation for a derive macro to encode typed data according to EIP-712, https://eips.ethereum.org/EIPS/eip-712 Additionally, this commit introduces a new signer trait method: async fn sign_typed_data<T: Eip712 + Send + Sync>( &self, payload: &T, ) -> Result<Signature, Self::Error>; And implements the new method for each of the signers (wallet, ledger, aws). Additionally, these changes include using `WalletError` for the Wallet signer error type At the moment, derive does not recurse the primary type to find nested Eip712 structs. This is something that is noted in the source and currently responds with an error regarding custom types. A subsequent PR should be opened once this issue becomes needed. For the moment, the current implementation should satisfy non-nested, basic struct types. * rename to ethers-derive-eip712; move to ethers-core * refactor of derive-eip712 macro; use ParamType and EthAbiToken * macro updates; add byte array checker for paramtype; use literal constant for domain type hash * replace std::convert::Infallible with WalletError as Wallet signer error type * update workspace members and dev dependencies for examples folder * add example for eip712 and test against contract * remove extraneous backward slash in '\x19\x01' prefix; example tests pass * update unreleased change log * remove print statements * use parse_macro_input macro; remove dead code; handle nest struct not implemented error * move eip712 example to solidity-contract tests folder; update cargo workspace dependencies * allow optional EIP712Domain parameter when encoding eip712 struct and signing typed data * add documentation for eip712 feature * Update ethers-signers/src/ledger/mod.rs Co-authored-by: Sebastian Martinez <me@sebastinez.dev> * add error enum for Eip712Error; use sign_payload for ledger signer * add EIP712WithDomain type for providing a wrapper around custom setting of the domain * make LedgerWallet sign_payload public * use optional feature gated dependencies for eip712; add default method for encode_eip712 * add default domain_separator method, pre-compute separator hash * move derive-eip712 deps to dev deps * remove invalid sign payload parameter, add await on async method * remove deprecated comment * debugging 'bad key handle' error for ledger signer try using 'sign_message' * await sign digest for aws signer * remove extra space, fix fmt warning * fix test, fmt errors * use gt 0.6.0 pragma compiler version * enable ABIEncoderV2 for solidity test contract * chore: make test constructor public Co-authored-by: Sebastian Martinez <me@sebastinez.dev> Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
2021-10-08 15:22:51 +00:00
use ethers_derive_eip712::*;
use ethers_providers::{Http, Middleware, PendingTransaction, Provider, StreamExt};
derive-eip712: initial implementation of eip712 derive macro (#481) * derive-eip712: initial implementation of eip712 derive macro This commit provides an initial implementation for a derive macro to encode typed data according to EIP-712, https://eips.ethereum.org/EIPS/eip-712 Additionally, this commit introduces a new signer trait method: async fn sign_typed_data<T: Eip712 + Send + Sync>( &self, payload: &T, ) -> Result<Signature, Self::Error>; And implements the new method for each of the signers (wallet, ledger, aws). Additionally, these changes include using `WalletError` for the Wallet signer error type At the moment, derive does not recurse the primary type to find nested Eip712 structs. This is something that is noted in the source and currently responds with an error regarding custom types. A subsequent PR should be opened once this issue becomes needed. For the moment, the current implementation should satisfy non-nested, basic struct types. * rename to ethers-derive-eip712; move to ethers-core * refactor of derive-eip712 macro; use ParamType and EthAbiToken * macro updates; add byte array checker for paramtype; use literal constant for domain type hash * replace std::convert::Infallible with WalletError as Wallet signer error type * update workspace members and dev dependencies for examples folder * add example for eip712 and test against contract * remove extraneous backward slash in '\x19\x01' prefix; example tests pass * update unreleased change log * remove print statements * use parse_macro_input macro; remove dead code; handle nest struct not implemented error * move eip712 example to solidity-contract tests folder; update cargo workspace dependencies * allow optional EIP712Domain parameter when encoding eip712 struct and signing typed data * add documentation for eip712 feature * Update ethers-signers/src/ledger/mod.rs Co-authored-by: Sebastian Martinez <me@sebastinez.dev> * add error enum for Eip712Error; use sign_payload for ledger signer * add EIP712WithDomain type for providing a wrapper around custom setting of the domain * make LedgerWallet sign_payload public * use optional feature gated dependencies for eip712; add default method for encode_eip712 * add default domain_separator method, pre-compute separator hash * move derive-eip712 deps to dev deps * remove invalid sign payload parameter, add await on async method * remove deprecated comment * debugging 'bad key handle' error for ledger signer try using 'sign_message' * await sign digest for aws signer * remove extra space, fix fmt warning * fix test, fmt errors * use gt 0.6.0 pragma compiler version * enable ABIEncoderV2 for solidity test contract * chore: make test constructor public Co-authored-by: Sebastian Martinez <me@sebastinez.dev> Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
2021-10-08 15:22:51 +00:00
use ethers_signers::{LocalWallet, Signer};
use std::{convert::TryFrom, iter::FromIterator, sync::Arc, time::Duration};
#[derive(Debug)]
pub struct NonClone<M> {
m: M,
}
#[derive(Debug)]
pub struct MwErr<M: Middleware>(M::Error);
impl<M> ethers_providers::FromErr<M::Error> for MwErr<M>
where
M: Middleware,
{
fn from(src: M::Error) -> Self {
Self(src)
}
}
impl<M: Middleware> std::fmt::Display for MwErr<M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Ok(())
}
}
impl<M: Middleware> std::error::Error for MwErr<M> {}
impl<M: Middleware> Middleware for NonClone<M> {
type Error = MwErr<M>;
type Provider = M::Provider;
type Inner = M;
fn inner(&self) -> &Self::Inner {
&self.m
}
}
// this is not a test. It is a compile check. :)
// It exists to ensure that trait bounds on contract internal behave as
// expected. It should not be run
fn it_compiles() {
let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
// launch anvil
let anvil = Anvil::new().spawn();
let client = Provider::<Http>::try_from(anvil.endpoint())
.unwrap()
.interval(Duration::from_millis(10u64));
// Works (B == M, M: Clone)
let c: ContractInstance<&Provider<Http>, Provider<Http>> =
ContractInstance::new(H160::default(), abi.clone(), &client);
let _ = c.method::<(), ()>("notARealMethod", ());
// Works (B == &M, M: Clone)
let c: ContractInstance<Provider<Http>, Provider<Http>> =
ContractInstance::new(H160::default(), abi.clone(), client.clone());
let _ = c.method::<(), ()>("notARealMethod", ());
let non_clone_mware = NonClone { m: client };
// Works (B == &M, M: !Clone)
let c: ContractInstance<&NonClone<Provider<Http>>, NonClone<Provider<Http>>> =
ContractInstance::new(H160::default(), abi, &non_clone_mware);
let _ = c.method::<(), ()>("notARealMethod", ());
// // Fails (B == M, M: !Clone)
// let c: ContractInternal<NonClone<Provider<Http>>, NonClone<Provider<Http>>> =
// ContractInternal::new(H160::default(), abi, non_clone_mware);
// let _ = c.method::<(), ()>("notARealMethod", ());
}
#[tokio::test]
async fn deploy_and_call_contract() {
let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
// launch anvil
let anvil = Anvil::new().spawn();
// Instantiate the clients. We assume that clients consume the provider and the wallet
// (which makes sense), so for multi-client tests, you must clone the provider.
let addrs = anvil.addresses().to_vec();
let addr2 = addrs[1];
let client = connect(&anvil, 0);
let client2 = connect(&anvil, 1);
// create a factory which will be used to deploy instances of the contract
let factory = ContractFactory::new(abi, bytecode, client.clone());
// `send` consumes the deployer so it must be cloned for later re-use
// (practically it's not expected that you'll need to deploy multiple instances of
// the _same_ deployer, so it's fine to clone here from a dev UX vs perf tradeoff)
let deployer = factory.deploy("initial value".to_string()).unwrap().legacy();
// dry runs the deployment of the contract. takes the deployer by reference, no need to
// clone.
deployer.call().await.unwrap();
let (contract, receipt) = deployer.clone().send_with_receipt().await.unwrap();
assert_eq!(receipt.contract_address.unwrap(), contract.address());
let get_value = contract.method::<_, String>("getValue", ()).unwrap();
let last_sender = contract.method::<_, Address>("lastSender", ()).unwrap();
// the initial value must be the one set in the constructor
let value = get_value.clone().call().await.unwrap();
assert_eq!(value, "initial value");
// need to declare the method first, and only then send it
// this is because it internally clones an Arc which would otherwise
// get immediately dropped
let contract_call = contract
.connect(client2.clone())
.method::<_, H256>("setValue", "hi".to_owned())
.unwrap();
let calldata = contract_call.calldata().unwrap();
let gas_estimate = contract_call.estimate_gas().await.unwrap();
let contract_call = contract_call.legacy();
let pending_tx = contract_call.send().await.unwrap();
let tx = client.get_transaction(*pending_tx).await.unwrap().unwrap();
let tx_receipt = pending_tx.await.unwrap().unwrap();
assert_eq!(last_sender.clone().call().await.unwrap(), addr2);
assert_eq!(get_value.clone().call().await.unwrap(), "hi");
assert_eq!(tx.input, calldata);
// we can also call contract methods at other addresses with the `at` call
// (useful when interacting with multiple ERC20s for example)
let contract2_addr = deployer.send().await.unwrap().address();
let contract2 = contract.at(contract2_addr);
let init_value: String =
contract2.method::<_, String>("getValue", ()).unwrap().call().await.unwrap();
let init_address =
contract2.method::<_, Address>("lastSender", ()).unwrap().call().await.unwrap();
assert_eq!(init_address, Address::zero());
assert_eq!(init_value, "initial value");
// methods with multiple args also work
let _tx_hash = contract
.method::<_, H256>("setValues", ("hi".to_owned(), "bye".to_owned()))
.unwrap()
.legacy()
.send()
.await
.unwrap()
.await
.unwrap();
}
#[tokio::test]
#[cfg(feature = "abigen")]
async fn get_past_events() {
let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
let anvil = Anvil::new().spawn();
let client = connect(&anvil, 0);
let address = client.get_accounts().await.unwrap()[0];
let contract = deploy(client.clone(), abi, bytecode).await;
// make a call with `client`
let func = contract.method::<_, H256>("setValue", "hi".to_owned()).unwrap().legacy();
let tx = func.send().await.unwrap();
let _receipt = tx.await.unwrap();
// and we can fetch the events
let logs: Vec<ValueChanged> = contract
.event()
.from_block(0u64)
.topic1(address) // Corresponds to the first indexed parameter
.query()
.await
.unwrap();
assert_eq!(logs[0].new_value, "initial value");
assert_eq!(logs[1].new_value, "hi");
assert_eq!(logs.len(), 2);
// and we can fetch the events at a block hash
let hash = client.get_block(1).await.unwrap().unwrap().hash.unwrap();
let logs: Vec<ValueChanged> = contract
.event()
.at_block_hash(hash)
.topic1(address) // Corresponds to the first indexed parameter
.query()
.await
.unwrap();
assert_eq!(logs[0].new_value, "initial value");
assert_eq!(logs.len(), 1);
}
#[tokio::test]
#[cfg(feature = "abigen")]
async fn get_events_with_meta() {
let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
let anvil = Anvil::new().spawn();
let client = connect(&anvil, 0);
let address = anvil.addresses()[0];
let contract = deploy(client.clone(), abi, bytecode).await;
// and we can fetch the events
let logs: Vec<(ValueChanged, LogMeta)> = contract
.event()
.from_block(0u64)
.topic1(address) // Corresponds to the first indexed parameter
.query_with_meta()
.await
.unwrap();
assert_eq!(logs.len(), 1);
let (log, meta) = &logs[0];
assert_eq!(log.new_value, "initial value");
assert_eq!(meta.address, contract.address());
assert_eq!(meta.log_index, 0.into());
assert_eq!(meta.block_number, 1.into());
let block = client.get_block(1).await.unwrap().unwrap();
assert_eq!(meta.block_hash, block.hash.unwrap());
assert_eq!(block.transactions.len(), 1);
let tx = block.transactions[0];
assert_eq!(meta.transaction_hash, tx);
assert_eq!(meta.transaction_index, 0.into());
}
#[tokio::test]
async fn call_past_state() {
let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
let anvil = Anvil::new().spawn();
let client = connect(&anvil, 0);
let contract = deploy(client.clone(), abi, bytecode).await;
let deployed_block = client.get_block_number().await.unwrap();
// assert initial state
let value =
contract.method::<_, String>("getValue", ()).unwrap().legacy().call().await.unwrap();
assert_eq!(value, "initial value");
// make a call with `client`
let _tx_hash = *contract
.method::<_, H256>("setValue", "hi".to_owned())
.unwrap()
.legacy()
.send()
.await
.unwrap();
// assert new value
let value =
contract.method::<_, String>("getValue", ()).unwrap().legacy().call().await.unwrap();
assert_eq!(value, "hi");
// assert previous value
let value = contract
.method::<_, String>("getValue", ())
.unwrap()
.legacy()
.block(BlockId::Number(deployed_block.into()))
.call()
.await
.unwrap();
assert_eq!(value, "initial value");
// Here would be the place to test EIP-1898, specifying the `BlockId` of `call` as the
// first block hash. However, Ganache does not implement this :/
// let hash = client.get_block(1).await.unwrap().unwrap().hash.unwrap();
// let value = contract
// .method::<_, String>("getValue", ())
// .unwrap()
// .block(BlockId::Hash(hash))
// .call()
// .await
// .unwrap();
// assert_eq!(value, "initial value");
}
#[tokio::test]
#[ignore]
async fn call_past_hash_test() {
// geth --dev --http --http.api eth,web3
let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
let provider = Provider::<Http>::try_from("http://localhost:8545").unwrap();
let deployer = provider.get_accounts().await.unwrap()[0];
let client = Arc::new(provider.with_sender(deployer));
let contract = deploy(client.clone(), abi, bytecode).await;
let deployed_block = client.get_block_number().await.unwrap();
// assert initial state
let value = contract.method::<_, String>("getValue", ()).unwrap().call().await.unwrap();
assert_eq!(value, "initial value");
// make a call with `client`
let _tx_hash =
*contract.method::<_, H256>("setValue", "hi".to_owned()).unwrap().send().await.unwrap();
// assert new value
let value = contract.method::<_, String>("getValue", ()).unwrap().call().await.unwrap();
assert_eq!(value, "hi");
// assert previous value using block hash
let hash = client.get_block(deployed_block).await.unwrap().unwrap().hash.unwrap();
let value = contract
.method::<_, String>("getValue", ())
.unwrap()
.block(BlockId::Hash(hash))
.call()
.await
.unwrap();
assert_eq!(value, "initial value");
}
#[tokio::test]
#[cfg(feature = "abigen")]
async fn watch_events() {
let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
let anvil = Anvil::new().spawn();
let client = connect(&anvil, 0);
2021-07-30 11:01:38 +00:00
let contract = deploy(client.clone(), abi.clone(), bytecode).await;
// We spawn the event listener:
let event = contract.event::<ValueChanged>();
let mut stream = event.stream().await.unwrap();
// Also set up a subscription for the same thing
let ws = Provider::connect(anvil.ws_endpoint()).await.unwrap();
let contract2 = ethers_contract::Contract::new(contract.address(), abi, ws.into());
let event2 = contract2.event::<ValueChanged>();
let mut subscription = event2.subscribe().await.unwrap();
2021-07-30 11:01:38 +00:00
let mut subscription_meta = event2.subscribe().await.unwrap().with_meta();
let num_calls = 3u64;
// and we make a few calls
2021-07-30 11:01:38 +00:00
let num = client.get_block_number().await.unwrap();
for i in 0..num_calls {
let call = contract.method::<_, H256>("setValue", i.to_string()).unwrap().legacy();
let pending_tx = call.send().await.unwrap();
let _receipt = pending_tx.await.unwrap();
}
for i in 0..num_calls {
// unwrap the option of the stream, then unwrap the decoding result
let log = stream.next().await.unwrap().unwrap();
let log2 = subscription.next().await.unwrap().unwrap();
2021-07-30 11:01:38 +00:00
let (log3, meta) = subscription_meta.next().await.unwrap().unwrap();
assert_eq!(log.new_value, log3.new_value);
assert_eq!(log.new_value, log2.new_value);
assert_eq!(log.new_value, i.to_string());
2021-07-30 11:01:38 +00:00
assert_eq!(meta.block_number, num + i + 1);
let hash = client.get_block(num + i + 1).await.unwrap().unwrap().hash.unwrap();
2021-07-30 11:01:38 +00:00
assert_eq!(meta.block_hash, hash);
}
}
#[tokio::test]
async fn watch_subscription_events_multiple_addresses() {
let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
let anvil = Anvil::new().spawn();
let client = connect(&anvil, 0);
let contract_1 = deploy(client.clone(), abi.clone(), bytecode.clone()).await;
let contract_2 = deploy(client.clone(), abi.clone(), bytecode).await;
let ws = Provider::connect(anvil.ws_endpoint()).await.unwrap();
let filter = Filter::new()
.address(ValueOrArray::Array(vec![contract_1.address(), contract_2.address()]));
let mut stream = ws.subscribe_logs(&filter).await.unwrap();
// and we make a few calls
let call = contract_1.method::<_, H256>("setValue", "1".to_string()).unwrap().legacy();
let pending_tx = call.send().await.unwrap();
let _receipt = pending_tx.await.unwrap();
let call = contract_2.method::<_, H256>("setValue", "2".to_string()).unwrap().legacy();
let pending_tx = call.send().await.unwrap();
let _receipt = pending_tx.await.unwrap();
// unwrap the option of the stream, then unwrap the decoding result
let log_1 = stream.next().await.unwrap();
let log_2 = stream.next().await.unwrap();
assert_eq!(log_1.address, contract_1.address());
assert_eq!(log_2.address, contract_2.address());
}
#[tokio::test]
async fn build_event_of_type() {
abigen!(
AggregatorInterface,
r#"[
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt)
]"#,
);
let anvil = Anvil::new().spawn();
let client = connect(&anvil, 0);
let event = ethers_contract::Contract::event_of_type::<AnswerUpdatedFilter>(&client);
assert_eq!(event.filter, Filter::new().event(&AnswerUpdatedFilter::abi_signature()));
}
#[tokio::test]
async fn signer_on_node() {
let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
// spawn anvil
let anvil = Anvil::new().spawn();
// connect
let provider = Provider::<Http>::try_from(anvil.endpoint())
.unwrap()
.interval(std::time::Duration::from_millis(50u64));
// get the first account
let deployer = provider.get_accounts().await.unwrap()[0];
let client = Arc::new(provider.with_sender(deployer));
let contract = deploy(client, abi, bytecode).await;
// make a call without the signer
let _receipt = contract
.method::<_, H256>("setValue", "hi".to_owned())
.unwrap()
.legacy()
.send()
.await
.unwrap()
.await
.unwrap();
let value: String =
contract.method::<_, String>("getValue", ()).unwrap().call().await.unwrap();
assert_eq!(value, "hi");
}
#[tokio::test]
async fn multicall_aggregate() {
// get ABI and bytecode for the Multicall contract
let (multicall_abi, multicall_bytecode) = compile_contract("Multicall3", "Multicall.sol");
// get ABI and bytecode for the NotSoSimpleStorage contract
let (not_so_simple_abi, not_so_simple_bytecode) =
compile_contract("NotSoSimpleStorage", "NotSoSimpleStorage.sol");
// get ABI and bytecode for the SimpleStorage contract
let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
// launch anvil
let anvil = Anvil::new().spawn();
// Instantiate the clients. We assume that clients consume the provider and the wallet
// (which makes sense), so for multi-client tests, you must clone the provider.
// `client` is used to deploy the Multicall contract
// `client2` is used to deploy the first SimpleStorage contract
// `client3` is used to deploy the second SimpleStorage contract
// `client4` is used to make the aggregate call
let addrs = anvil.addresses().to_vec();
let addr2 = addrs[1];
let addr3 = addrs[2];
let client = connect(&anvil, 0);
let client2 = connect(&anvil, 1);
let client3 = connect(&anvil, 2);
let client4 = connect(&anvil, 3);
// create a factory which will be used to deploy instances of the contract
let multicall_factory =
ContractFactory::new(multicall_abi, multicall_bytecode, client.clone());
let simple_factory = ContractFactory::new(abi.clone(), bytecode.clone(), client2.clone());
let not_so_simple_factory =
ContractFactory::new(not_so_simple_abi, not_so_simple_bytecode, client3.clone());
let multicall_contract =
multicall_factory.deploy(()).unwrap().legacy().send().await.unwrap();
let addr = multicall_contract.address();
let simple_contract = simple_factory
.deploy("the first one".to_string())
.unwrap()
.legacy()
.send()
.await
.unwrap();
let not_so_simple_contract = not_so_simple_factory
.deploy("the second one".to_string())
.unwrap()
.legacy()
.send()
.await
.unwrap();
// Client2 and Client3 broadcast txs to set the values for both contracts
simple_contract
.connect(client2.clone())
.method::<_, H256>("setValue", "reset first".to_owned())
.unwrap()
.legacy()
.send()
.await
.unwrap();
not_so_simple_contract
.connect(client3.clone())
.method::<_, H256>("setValue", "reset second".to_owned())
.unwrap()
.legacy()
.send()
.await
.unwrap();
// get the calls for `value` and `last_sender` for both SimpleStorage contracts
let value = simple_contract.method::<_, String>("getValue", ()).unwrap();
let value2 =
not_so_simple_contract.method::<_, (String, Address)>("getValues", ()).unwrap();
let last_sender = simple_contract.method::<_, Address>("lastSender", ()).unwrap();
let last_sender2 = not_so_simple_contract.method::<_, Address>("lastSender", ()).unwrap();
// initiate the Multicall instance and add calls one by one in builder style
let mut multicall = Multicall::new(client4.clone(), Some(addr)).await.unwrap();
// Set version to 1
multicall = multicall.version(MulticallVersion::Multicall);
multicall
.add_call(value, false)
.add_call(value2, false)
.add_call(last_sender, false)
.add_call(last_sender2, false);
let return_data: (String, (String, Address), Address, Address) =
multicall.call().await.unwrap();
assert_eq!(return_data.0, "reset first");
assert_eq!((return_data.1).0, "reset second");
assert_eq!((return_data.1).1, addr3);
assert_eq!(return_data.2, addr2);
assert_eq!(return_data.3, addr3);
// construct broadcast transactions that will be batched and broadcast via Multicall
let broadcast = simple_contract
.connect(client4.clone())
.method::<_, H256>("setValue", "first reset again".to_owned())
.unwrap();
let broadcast2 = not_so_simple_contract
.connect(client4.clone())
.method::<_, H256>("setValue", "second reset again".to_owned())
.unwrap();
// use the already initialised Multicall instance, clearing the previous calls and adding
// new calls. Previously we used the `.call()` functionality to do a batch of calls in one
// go. Now we will use the `.send()` functionality to broadcast a batch of transactions
// in one go
let mut multicall_send = multicall.clone();
multicall_send.clear_calls().add_call(broadcast, false).add_call(broadcast2, false);
// broadcast the transaction and wait for it to be mined
let _tx_receipt = multicall_send.legacy().send().await.unwrap().await.unwrap();
// Do another multicall to check the updated return values
// The `getValue` calls should return the last value we set in the batched broadcast
// The `lastSender` calls should return the address of the Multicall contract, as it is
// the one acting as proxy and calling our SimpleStorage contracts (msg.sender)
let return_data: (String, (String, Address), Address, Address) =
multicall.call().await.unwrap();
assert_eq!(return_data.0, "first reset again");
assert_eq!((return_data.1).0, "second reset again");
assert_eq!((return_data.1).1, multicall_contract.address());
assert_eq!(return_data.2, multicall_contract.address());
assert_eq!(return_data.3, multicall_contract.address());
let addrs = anvil.addresses();
// query ETH balances of multiple addresses
// these keys haven't been used to do any tx
// so should have 100 ETH
multicall
.clear_calls()
.add_get_eth_balance(addrs[4], false)
.add_get_eth_balance(addrs[5], false)
.add_get_eth_balance(addrs[6], false);
let valid_balances = [
U256::from(10_000_000_000_000_000_000_000u128),
U256::from(10_000_000_000_000_000_000_000u128),
U256::from(10_000_000_000_000_000_000_000u128),
];
let balances: (U256, U256, U256) = multicall.call().await.unwrap();
assert_eq!(balances.0, valid_balances[0]);
assert_eq!(balances.1, valid_balances[1]);
assert_eq!(balances.2, valid_balances[2]);
// call_array
multicall
.clear_calls()
.add_get_eth_balance(addrs[4], false)
.add_get_eth_balance(addrs[5], false)
.add_get_eth_balance(addrs[6], false);
let balances: Vec<U256> = multicall.call_array().await.unwrap();
assert_eq!(balances, Vec::from_iter(valid_balances.iter().copied()));
// clear multicall so we can test `call_raw` w/ >16 calls
multicall.clear_calls();
// clear the current value
simple_contract
.connect(client2.clone())
.method::<_, H256>("setValue", "many".to_owned())
.unwrap()
.legacy()
.send()
.await
.unwrap();
// build up a list of calls greater than the 16 max restriction
multicall.add_calls(
false,
std::iter::repeat(simple_contract.method::<_, String>("getValue", ()).unwrap())
.take(17), // .collect(),
);
// must use `call_raw` as `.calls` > 16
let tokens = multicall.call_raw().await.unwrap();
// if want to use, must detokenize manually
let results: Vec<String> = tokens
.iter()
.map(|token| {
// decode manually using Tokenizable method
String::from_token(token.to_owned()).unwrap()
})
.collect();
assert_eq!(results, ["many"; 17]);
// test version 2
multicall = multicall.version(MulticallVersion::Multicall2);
// deploy contract with reverting methods
let reverting_contract = {
let (abi, bytecode) =
compile_contract("SimpleRevertingStorage", "SimpleRevertingStorage.sol");
let f = ContractFactory::new(abi, bytecode, client.clone());
f.deploy("This contract can revert".to_string()).unwrap().send().await.unwrap()
};
// reset value
reverting_contract
.connect(client2.clone())
.method::<_, H256>("setValue", ("reset third".to_owned(), false))
.unwrap()
.send()
.await
.unwrap();
// create calls
let set_value_call = reverting_contract
.connect(client.clone())
.method::<_, H256>("setValue", ("this didn't revert".to_owned(), false))
.unwrap();
let set_value_reverting_call = reverting_contract
.connect(client3.clone())
.method::<_, H256>("setValue", ("this reverted".to_owned(), true))
.unwrap();
let get_value_call = reverting_contract
.connect(client2.clone())
.method::<_, String>("getValue", (false))
.unwrap();
let get_value_reverting_call = reverting_contract
.connect(client.clone())
.method::<_, String>("getValue", (true))
.unwrap();
// .send reverts
// don't allow revert
multicall
.clear_calls()
.add_call(set_value_reverting_call.clone(), false)
.add_call(set_value_call.clone(), false);
multicall.send().await.unwrap_err();
// value has not changed
assert_eq!(get_value_call.clone().call().await.unwrap(), "reset third");
// allow revert
multicall
.clear_calls()
.add_call(set_value_reverting_call.clone(), true)
.add_call(set_value_call.clone(), false);
multicall.send().await.unwrap();
// value has changed
assert_eq!(get_value_call.clone().call().await.unwrap(), "this didn't revert");
// reset value again
reverting_contract
.connect(client2.clone())
.method::<_, H256>("setValue", ("reset third again".to_owned(), false))
.unwrap()
.send()
.await
.unwrap();
// .call reverts
// don't allow revert
multicall
.clear_calls()
.add_call(get_value_reverting_call.clone(), false)
.add_call(get_value_call.clone(), false);
let res = multicall.call::<((bool, String), (bool, String))>().await;
let err = res.unwrap_err();
assert!(err.to_string().contains("Multicall3: call failed"));
// allow revert
multicall
.clear_calls()
.add_call(get_value_reverting_call.clone(), true)
.add_call(get_value_call.clone(), false);
let res = multicall.call().await;
let data: ((bool, String), (bool, String)) = res.unwrap();
assert!(!data.0 .0); // first call reverted
assert_eq!(data.0 .1, "getValue revert"); // first call revert data
assert!(data.1 .0); // second call didn't revert
assert_eq!(data.1 .1, "reset third again"); // second call return data
// test v2 illegal revert
multicall
.clear_calls()
.add_call(get_value_reverting_call.clone(), false) // don't allow revert
.add_call(get_value_call.clone(), true); // true here will result in `tryAggregate(false, ...)`
let res = multicall.call::<((bool, String), (bool, String))>().await;
let err = res.unwrap_err();
assert!(err.to_string().contains("Illegal revert"));
// test version 3
// aggregate3 is the same as try_aggregate except with allowing failure on a per-call basis.
// no need to test that
multicall = multicall.version(MulticallVersion::Multicall3);
// .send with value
let amount = U256::from(100);
let value_tx = reverting_contract.method::<_, H256>("deposit", ()).unwrap().value(amount);
let rc_addr = reverting_contract.address();
// add a second call because we can't decode using a single element tuple
// ((bool, U256)) == (bool, U256)
let bal_before: ((bool, U256), (bool, U256)) = multicall
.clear_calls()
.add_get_eth_balance(rc_addr, false)
.add_get_eth_balance(rc_addr, false)
.call()
.await
.unwrap();
// send 2 value_tx
multicall.clear_calls().add_call(value_tx.clone(), false).add_call(value_tx.clone(), false);
multicall.send().await.unwrap();
let bal_after: ((bool, U256), (bool, U256)) = multicall
.clear_calls()
.add_get_eth_balance(rc_addr, false)
.add_get_eth_balance(rc_addr, false)
.call()
.await
.unwrap();
assert_eq!(bal_after.0 .1, bal_before.0 .1 + U256::from(2) * amount);
// test specific revert cases
// empty revert
let empty_revert = reverting_contract.method::<_, H256>("emptyRevert", ()).unwrap();
multicall
.clear_calls()
.add_call(empty_revert.clone(), true)
.add_call(empty_revert.clone(), true);
let res: ((bool, Bytes), (bool, Bytes)) = multicall.call().await.unwrap();
assert!(!res.0 .0);
assert_eq!(res.0 .1, Bytes::default());
// string revert
let string_revert =
reverting_contract.method::<_, H256>("stringRevert", ("String".to_string())).unwrap();
multicall.clear_calls().add_call(string_revert, true).add_call(empty_revert.clone(), true);
let res: ((bool, String), (bool, Bytes)) = multicall.call().await.unwrap();
assert!(!res.0 .0);
assert_eq!(res.0 .1, "String");
// custom error revert
let custom_error = reverting_contract.method::<_, H256>("customError", ()).unwrap();
multicall.clear_calls().add_call(custom_error, true).add_call(empty_revert.clone(), true);
let res: ((bool, Bytes), (bool, Bytes)) = multicall.call().await.unwrap();
let selector = &keccak256("CustomError()")[..4];
assert!(!res.0 .0);
assert_eq!(res.0 .1.len(), 4);
assert_eq!(&res.0 .1[..4], selector);
// custom error with data revert
let custom_error_with_data = reverting_contract
.method::<_, H256>("customErrorWithData", ("Data".to_string()))
.unwrap();
multicall
.clear_calls()
.add_call(custom_error_with_data, true)
.add_call(empty_revert.clone(), true);
let res: ((bool, Bytes), (bool, Bytes)) = multicall.call().await.unwrap();
let selector = &keccak256("CustomErrorWithData(string)")[..4];
assert!(!res.0 .0);
assert_eq!(&res.0 .1[..4], selector);
assert_eq!(&res.0 .1[4..], encode(&[Token::String("Data".to_string())]));
}
derive-eip712: initial implementation of eip712 derive macro (#481) * derive-eip712: initial implementation of eip712 derive macro This commit provides an initial implementation for a derive macro to encode typed data according to EIP-712, https://eips.ethereum.org/EIPS/eip-712 Additionally, this commit introduces a new signer trait method: async fn sign_typed_data<T: Eip712 + Send + Sync>( &self, payload: &T, ) -> Result<Signature, Self::Error>; And implements the new method for each of the signers (wallet, ledger, aws). Additionally, these changes include using `WalletError` for the Wallet signer error type At the moment, derive does not recurse the primary type to find nested Eip712 structs. This is something that is noted in the source and currently responds with an error regarding custom types. A subsequent PR should be opened once this issue becomes needed. For the moment, the current implementation should satisfy non-nested, basic struct types. * rename to ethers-derive-eip712; move to ethers-core * refactor of derive-eip712 macro; use ParamType and EthAbiToken * macro updates; add byte array checker for paramtype; use literal constant for domain type hash * replace std::convert::Infallible with WalletError as Wallet signer error type * update workspace members and dev dependencies for examples folder * add example for eip712 and test against contract * remove extraneous backward slash in '\x19\x01' prefix; example tests pass * update unreleased change log * remove print statements * use parse_macro_input macro; remove dead code; handle nest struct not implemented error * move eip712 example to solidity-contract tests folder; update cargo workspace dependencies * allow optional EIP712Domain parameter when encoding eip712 struct and signing typed data * add documentation for eip712 feature * Update ethers-signers/src/ledger/mod.rs Co-authored-by: Sebastian Martinez <me@sebastinez.dev> * add error enum for Eip712Error; use sign_payload for ledger signer * add EIP712WithDomain type for providing a wrapper around custom setting of the domain * make LedgerWallet sign_payload public * use optional feature gated dependencies for eip712; add default method for encode_eip712 * add default domain_separator method, pre-compute separator hash * move derive-eip712 deps to dev deps * remove invalid sign payload parameter, add await on async method * remove deprecated comment * debugging 'bad key handle' error for ledger signer try using 'sign_message' * await sign digest for aws signer * remove extra space, fix fmt warning * fix test, fmt errors * use gt 0.6.0 pragma compiler version * enable ABIEncoderV2 for solidity test contract * chore: make test constructor public Co-authored-by: Sebastian Martinez <me@sebastinez.dev> Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
2021-10-08 15:22:51 +00:00
#[tokio::test]
async fn test_derive_eip712() {
// Generate Contract ABI Bindings
abigen!(
DeriveEip712Test,
"./ethers-contract/tests/solidity-contracts/derive_eip712_abi.json",
event_derives(serde::Deserialize, serde::Serialize)
);
// Create derived structs
#[derive(Debug, Clone, Eip712, EthAbiType)]
#[eip712(
name = "Eip712Test",
version = "1",
chain_id = 1,
verifying_contract = "0x0000000000000000000000000000000000000001",
salt = "eip712-test-75F0CCte"
)]
struct FooBar {
foo: I256,
bar: U256,
fizz: Bytes,
derive-eip712: initial implementation of eip712 derive macro (#481) * derive-eip712: initial implementation of eip712 derive macro This commit provides an initial implementation for a derive macro to encode typed data according to EIP-712, https://eips.ethereum.org/EIPS/eip-712 Additionally, this commit introduces a new signer trait method: async fn sign_typed_data<T: Eip712 + Send + Sync>( &self, payload: &T, ) -> Result<Signature, Self::Error>; And implements the new method for each of the signers (wallet, ledger, aws). Additionally, these changes include using `WalletError` for the Wallet signer error type At the moment, derive does not recurse the primary type to find nested Eip712 structs. This is something that is noted in the source and currently responds with an error regarding custom types. A subsequent PR should be opened once this issue becomes needed. For the moment, the current implementation should satisfy non-nested, basic struct types. * rename to ethers-derive-eip712; move to ethers-core * refactor of derive-eip712 macro; use ParamType and EthAbiToken * macro updates; add byte array checker for paramtype; use literal constant for domain type hash * replace std::convert::Infallible with WalletError as Wallet signer error type * update workspace members and dev dependencies for examples folder * add example for eip712 and test against contract * remove extraneous backward slash in '\x19\x01' prefix; example tests pass * update unreleased change log * remove print statements * use parse_macro_input macro; remove dead code; handle nest struct not implemented error * move eip712 example to solidity-contract tests folder; update cargo workspace dependencies * allow optional EIP712Domain parameter when encoding eip712 struct and signing typed data * add documentation for eip712 feature * Update ethers-signers/src/ledger/mod.rs Co-authored-by: Sebastian Martinez <me@sebastinez.dev> * add error enum for Eip712Error; use sign_payload for ledger signer * add EIP712WithDomain type for providing a wrapper around custom setting of the domain * make LedgerWallet sign_payload public * use optional feature gated dependencies for eip712; add default method for encode_eip712 * add default domain_separator method, pre-compute separator hash * move derive-eip712 deps to dev deps * remove invalid sign payload parameter, add await on async method * remove deprecated comment * debugging 'bad key handle' error for ledger signer try using 'sign_message' * await sign digest for aws signer * remove extra space, fix fmt warning * fix test, fmt errors * use gt 0.6.0 pragma compiler version * enable ABIEncoderV2 for solidity test contract * chore: make test constructor public Co-authored-by: Sebastian Martinez <me@sebastinez.dev> Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
2021-10-08 15:22:51 +00:00
buzz: [u8; 32],
far: String,
out: Address,
}
// get ABI and bytecode for the DeriveEip712Test contract
let (abi, bytecode) = compile_contract("DeriveEip712Test", "DeriveEip712Test.sol");
// launch the network & connect to it
let anvil = Anvil::new().spawn();
let from = anvil.addresses()[0];
let provider = Provider::try_from(anvil.endpoint())
.unwrap()
.with_sender(from)
.interval(std::time::Duration::from_millis(10));
let client = Arc::new(provider);
derive-eip712: initial implementation of eip712 derive macro (#481) * derive-eip712: initial implementation of eip712 derive macro This commit provides an initial implementation for a derive macro to encode typed data according to EIP-712, https://eips.ethereum.org/EIPS/eip-712 Additionally, this commit introduces a new signer trait method: async fn sign_typed_data<T: Eip712 + Send + Sync>( &self, payload: &T, ) -> Result<Signature, Self::Error>; And implements the new method for each of the signers (wallet, ledger, aws). Additionally, these changes include using `WalletError` for the Wallet signer error type At the moment, derive does not recurse the primary type to find nested Eip712 structs. This is something that is noted in the source and currently responds with an error regarding custom types. A subsequent PR should be opened once this issue becomes needed. For the moment, the current implementation should satisfy non-nested, basic struct types. * rename to ethers-derive-eip712; move to ethers-core * refactor of derive-eip712 macro; use ParamType and EthAbiToken * macro updates; add byte array checker for paramtype; use literal constant for domain type hash * replace std::convert::Infallible with WalletError as Wallet signer error type * update workspace members and dev dependencies for examples folder * add example for eip712 and test against contract * remove extraneous backward slash in '\x19\x01' prefix; example tests pass * update unreleased change log * remove print statements * use parse_macro_input macro; remove dead code; handle nest struct not implemented error * move eip712 example to solidity-contract tests folder; update cargo workspace dependencies * allow optional EIP712Domain parameter when encoding eip712 struct and signing typed data * add documentation for eip712 feature * Update ethers-signers/src/ledger/mod.rs Co-authored-by: Sebastian Martinez <me@sebastinez.dev> * add error enum for Eip712Error; use sign_payload for ledger signer * add EIP712WithDomain type for providing a wrapper around custom setting of the domain * make LedgerWallet sign_payload public * use optional feature gated dependencies for eip712; add default method for encode_eip712 * add default domain_separator method, pre-compute separator hash * move derive-eip712 deps to dev deps * remove invalid sign payload parameter, add await on async method * remove deprecated comment * debugging 'bad key handle' error for ledger signer try using 'sign_message' * await sign digest for aws signer * remove extra space, fix fmt warning * fix test, fmt errors * use gt 0.6.0 pragma compiler version * enable ABIEncoderV2 for solidity test contract * chore: make test constructor public Co-authored-by: Sebastian Martinez <me@sebastinez.dev> Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
2021-10-08 15:22:51 +00:00
let wallet: LocalWallet = anvil.keys()[0].clone().into();
derive-eip712: initial implementation of eip712 derive macro (#481) * derive-eip712: initial implementation of eip712 derive macro This commit provides an initial implementation for a derive macro to encode typed data according to EIP-712, https://eips.ethereum.org/EIPS/eip-712 Additionally, this commit introduces a new signer trait method: async fn sign_typed_data<T: Eip712 + Send + Sync>( &self, payload: &T, ) -> Result<Signature, Self::Error>; And implements the new method for each of the signers (wallet, ledger, aws). Additionally, these changes include using `WalletError` for the Wallet signer error type At the moment, derive does not recurse the primary type to find nested Eip712 structs. This is something that is noted in the source and currently responds with an error regarding custom types. A subsequent PR should be opened once this issue becomes needed. For the moment, the current implementation should satisfy non-nested, basic struct types. * rename to ethers-derive-eip712; move to ethers-core * refactor of derive-eip712 macro; use ParamType and EthAbiToken * macro updates; add byte array checker for paramtype; use literal constant for domain type hash * replace std::convert::Infallible with WalletError as Wallet signer error type * update workspace members and dev dependencies for examples folder * add example for eip712 and test against contract * remove extraneous backward slash in '\x19\x01' prefix; example tests pass * update unreleased change log * remove print statements * use parse_macro_input macro; remove dead code; handle nest struct not implemented error * move eip712 example to solidity-contract tests folder; update cargo workspace dependencies * allow optional EIP712Domain parameter when encoding eip712 struct and signing typed data * add documentation for eip712 feature * Update ethers-signers/src/ledger/mod.rs Co-authored-by: Sebastian Martinez <me@sebastinez.dev> * add error enum for Eip712Error; use sign_payload for ledger signer * add EIP712WithDomain type for providing a wrapper around custom setting of the domain * make LedgerWallet sign_payload public * use optional feature gated dependencies for eip712; add default method for encode_eip712 * add default domain_separator method, pre-compute separator hash * move derive-eip712 deps to dev deps * remove invalid sign payload parameter, add await on async method * remove deprecated comment * debugging 'bad key handle' error for ledger signer try using 'sign_message' * await sign digest for aws signer * remove extra space, fix fmt warning * fix test, fmt errors * use gt 0.6.0 pragma compiler version * enable ABIEncoderV2 for solidity test contract * chore: make test constructor public Co-authored-by: Sebastian Martinez <me@sebastinez.dev> Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
2021-10-08 15:22:51 +00:00
let factory = ContractFactory::new(abi.clone(), bytecode.clone(), client.clone());
let contract = factory
.deploy(())
.expect("failed to deploy DeriveEip712Test contract")
.legacy()
.send()
.await
.expect("failed to instantiate factory for DeriveEip712 contract");
let addr = contract.address();
let contract = DeriveEip712Test::new(addr, client.clone());
let foo_bar = FooBar {
foo: I256::from(10u64),
bar: U256::from(20u64),
fizz: b"fizz".into(),
derive-eip712: initial implementation of eip712 derive macro (#481) * derive-eip712: initial implementation of eip712 derive macro This commit provides an initial implementation for a derive macro to encode typed data according to EIP-712, https://eips.ethereum.org/EIPS/eip-712 Additionally, this commit introduces a new signer trait method: async fn sign_typed_data<T: Eip712 + Send + Sync>( &self, payload: &T, ) -> Result<Signature, Self::Error>; And implements the new method for each of the signers (wallet, ledger, aws). Additionally, these changes include using `WalletError` for the Wallet signer error type At the moment, derive does not recurse the primary type to find nested Eip712 structs. This is something that is noted in the source and currently responds with an error regarding custom types. A subsequent PR should be opened once this issue becomes needed. For the moment, the current implementation should satisfy non-nested, basic struct types. * rename to ethers-derive-eip712; move to ethers-core * refactor of derive-eip712 macro; use ParamType and EthAbiToken * macro updates; add byte array checker for paramtype; use literal constant for domain type hash * replace std::convert::Infallible with WalletError as Wallet signer error type * update workspace members and dev dependencies for examples folder * add example for eip712 and test against contract * remove extraneous backward slash in '\x19\x01' prefix; example tests pass * update unreleased change log * remove print statements * use parse_macro_input macro; remove dead code; handle nest struct not implemented error * move eip712 example to solidity-contract tests folder; update cargo workspace dependencies * allow optional EIP712Domain parameter when encoding eip712 struct and signing typed data * add documentation for eip712 feature * Update ethers-signers/src/ledger/mod.rs Co-authored-by: Sebastian Martinez <me@sebastinez.dev> * add error enum for Eip712Error; use sign_payload for ledger signer * add EIP712WithDomain type for providing a wrapper around custom setting of the domain * make LedgerWallet sign_payload public * use optional feature gated dependencies for eip712; add default method for encode_eip712 * add default domain_separator method, pre-compute separator hash * move derive-eip712 deps to dev deps * remove invalid sign payload parameter, add await on async method * remove deprecated comment * debugging 'bad key handle' error for ledger signer try using 'sign_message' * await sign digest for aws signer * remove extra space, fix fmt warning * fix test, fmt errors * use gt 0.6.0 pragma compiler version * enable ABIEncoderV2 for solidity test contract * chore: make test constructor public Co-authored-by: Sebastian Martinez <me@sebastinez.dev> Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
2021-10-08 15:22:51 +00:00
buzz: keccak256("buzz"),
far: String::from("space"),
out: Address::from([0; 20]),
};
let derived_foo_bar = derive_eip_712_test::FooBar {
foo: foo_bar.foo,
bar: foo_bar.bar,
derive-eip712: initial implementation of eip712 derive macro (#481) * derive-eip712: initial implementation of eip712 derive macro This commit provides an initial implementation for a derive macro to encode typed data according to EIP-712, https://eips.ethereum.org/EIPS/eip-712 Additionally, this commit introduces a new signer trait method: async fn sign_typed_data<T: Eip712 + Send + Sync>( &self, payload: &T, ) -> Result<Signature, Self::Error>; And implements the new method for each of the signers (wallet, ledger, aws). Additionally, these changes include using `WalletError` for the Wallet signer error type At the moment, derive does not recurse the primary type to find nested Eip712 structs. This is something that is noted in the source and currently responds with an error regarding custom types. A subsequent PR should be opened once this issue becomes needed. For the moment, the current implementation should satisfy non-nested, basic struct types. * rename to ethers-derive-eip712; move to ethers-core * refactor of derive-eip712 macro; use ParamType and EthAbiToken * macro updates; add byte array checker for paramtype; use literal constant for domain type hash * replace std::convert::Infallible with WalletError as Wallet signer error type * update workspace members and dev dependencies for examples folder * add example for eip712 and test against contract * remove extraneous backward slash in '\x19\x01' prefix; example tests pass * update unreleased change log * remove print statements * use parse_macro_input macro; remove dead code; handle nest struct not implemented error * move eip712 example to solidity-contract tests folder; update cargo workspace dependencies * allow optional EIP712Domain parameter when encoding eip712 struct and signing typed data * add documentation for eip712 feature * Update ethers-signers/src/ledger/mod.rs Co-authored-by: Sebastian Martinez <me@sebastinez.dev> * add error enum for Eip712Error; use sign_payload for ledger signer * add EIP712WithDomain type for providing a wrapper around custom setting of the domain * make LedgerWallet sign_payload public * use optional feature gated dependencies for eip712; add default method for encode_eip712 * add default domain_separator method, pre-compute separator hash * move derive-eip712 deps to dev deps * remove invalid sign payload parameter, add await on async method * remove deprecated comment * debugging 'bad key handle' error for ledger signer try using 'sign_message' * await sign digest for aws signer * remove extra space, fix fmt warning * fix test, fmt errors * use gt 0.6.0 pragma compiler version * enable ABIEncoderV2 for solidity test contract * chore: make test constructor public Co-authored-by: Sebastian Martinez <me@sebastinez.dev> Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
2021-10-08 15:22:51 +00:00
fizz: foo_bar.fizz.clone(),
buzz: foo_bar.buzz,
derive-eip712: initial implementation of eip712 derive macro (#481) * derive-eip712: initial implementation of eip712 derive macro This commit provides an initial implementation for a derive macro to encode typed data according to EIP-712, https://eips.ethereum.org/EIPS/eip-712 Additionally, this commit introduces a new signer trait method: async fn sign_typed_data<T: Eip712 + Send + Sync>( &self, payload: &T, ) -> Result<Signature, Self::Error>; And implements the new method for each of the signers (wallet, ledger, aws). Additionally, these changes include using `WalletError` for the Wallet signer error type At the moment, derive does not recurse the primary type to find nested Eip712 structs. This is something that is noted in the source and currently responds with an error regarding custom types. A subsequent PR should be opened once this issue becomes needed. For the moment, the current implementation should satisfy non-nested, basic struct types. * rename to ethers-derive-eip712; move to ethers-core * refactor of derive-eip712 macro; use ParamType and EthAbiToken * macro updates; add byte array checker for paramtype; use literal constant for domain type hash * replace std::convert::Infallible with WalletError as Wallet signer error type * update workspace members and dev dependencies for examples folder * add example for eip712 and test against contract * remove extraneous backward slash in '\x19\x01' prefix; example tests pass * update unreleased change log * remove print statements * use parse_macro_input macro; remove dead code; handle nest struct not implemented error * move eip712 example to solidity-contract tests folder; update cargo workspace dependencies * allow optional EIP712Domain parameter when encoding eip712 struct and signing typed data * add documentation for eip712 feature * Update ethers-signers/src/ledger/mod.rs Co-authored-by: Sebastian Martinez <me@sebastinez.dev> * add error enum for Eip712Error; use sign_payload for ledger signer * add EIP712WithDomain type for providing a wrapper around custom setting of the domain * make LedgerWallet sign_payload public * use optional feature gated dependencies for eip712; add default method for encode_eip712 * add default domain_separator method, pre-compute separator hash * move derive-eip712 deps to dev deps * remove invalid sign payload parameter, add await on async method * remove deprecated comment * debugging 'bad key handle' error for ledger signer try using 'sign_message' * await sign digest for aws signer * remove extra space, fix fmt warning * fix test, fmt errors * use gt 0.6.0 pragma compiler version * enable ABIEncoderV2 for solidity test contract * chore: make test constructor public Co-authored-by: Sebastian Martinez <me@sebastinez.dev> Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
2021-10-08 15:22:51 +00:00
far: foo_bar.far.clone(),
out: foo_bar.out,
derive-eip712: initial implementation of eip712 derive macro (#481) * derive-eip712: initial implementation of eip712 derive macro This commit provides an initial implementation for a derive macro to encode typed data according to EIP-712, https://eips.ethereum.org/EIPS/eip-712 Additionally, this commit introduces a new signer trait method: async fn sign_typed_data<T: Eip712 + Send + Sync>( &self, payload: &T, ) -> Result<Signature, Self::Error>; And implements the new method for each of the signers (wallet, ledger, aws). Additionally, these changes include using `WalletError` for the Wallet signer error type At the moment, derive does not recurse the primary type to find nested Eip712 structs. This is something that is noted in the source and currently responds with an error regarding custom types. A subsequent PR should be opened once this issue becomes needed. For the moment, the current implementation should satisfy non-nested, basic struct types. * rename to ethers-derive-eip712; move to ethers-core * refactor of derive-eip712 macro; use ParamType and EthAbiToken * macro updates; add byte array checker for paramtype; use literal constant for domain type hash * replace std::convert::Infallible with WalletError as Wallet signer error type * update workspace members and dev dependencies for examples folder * add example for eip712 and test against contract * remove extraneous backward slash in '\x19\x01' prefix; example tests pass * update unreleased change log * remove print statements * use parse_macro_input macro; remove dead code; handle nest struct not implemented error * move eip712 example to solidity-contract tests folder; update cargo workspace dependencies * allow optional EIP712Domain parameter when encoding eip712 struct and signing typed data * add documentation for eip712 feature * Update ethers-signers/src/ledger/mod.rs Co-authored-by: Sebastian Martinez <me@sebastinez.dev> * add error enum for Eip712Error; use sign_payload for ledger signer * add EIP712WithDomain type for providing a wrapper around custom setting of the domain * make LedgerWallet sign_payload public * use optional feature gated dependencies for eip712; add default method for encode_eip712 * add default domain_separator method, pre-compute separator hash * move derive-eip712 deps to dev deps * remove invalid sign payload parameter, add await on async method * remove deprecated comment * debugging 'bad key handle' error for ledger signer try using 'sign_message' * await sign digest for aws signer * remove extra space, fix fmt warning * fix test, fmt errors * use gt 0.6.0 pragma compiler version * enable ABIEncoderV2 for solidity test contract * chore: make test constructor public Co-authored-by: Sebastian Martinez <me@sebastinez.dev> Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
2021-10-08 15:22:51 +00:00
};
let sig = wallet.sign_typed_data(&foo_bar).await.expect("failed to sign typed data");
derive-eip712: initial implementation of eip712 derive macro (#481) * derive-eip712: initial implementation of eip712 derive macro This commit provides an initial implementation for a derive macro to encode typed data according to EIP-712, https://eips.ethereum.org/EIPS/eip-712 Additionally, this commit introduces a new signer trait method: async fn sign_typed_data<T: Eip712 + Send + Sync>( &self, payload: &T, ) -> Result<Signature, Self::Error>; And implements the new method for each of the signers (wallet, ledger, aws). Additionally, these changes include using `WalletError` for the Wallet signer error type At the moment, derive does not recurse the primary type to find nested Eip712 structs. This is something that is noted in the source and currently responds with an error regarding custom types. A subsequent PR should be opened once this issue becomes needed. For the moment, the current implementation should satisfy non-nested, basic struct types. * rename to ethers-derive-eip712; move to ethers-core * refactor of derive-eip712 macro; use ParamType and EthAbiToken * macro updates; add byte array checker for paramtype; use literal constant for domain type hash * replace std::convert::Infallible with WalletError as Wallet signer error type * update workspace members and dev dependencies for examples folder * add example for eip712 and test against contract * remove extraneous backward slash in '\x19\x01' prefix; example tests pass * update unreleased change log * remove print statements * use parse_macro_input macro; remove dead code; handle nest struct not implemented error * move eip712 example to solidity-contract tests folder; update cargo workspace dependencies * allow optional EIP712Domain parameter when encoding eip712 struct and signing typed data * add documentation for eip712 feature * Update ethers-signers/src/ledger/mod.rs Co-authored-by: Sebastian Martinez <me@sebastinez.dev> * add error enum for Eip712Error; use sign_payload for ledger signer * add EIP712WithDomain type for providing a wrapper around custom setting of the domain * make LedgerWallet sign_payload public * use optional feature gated dependencies for eip712; add default method for encode_eip712 * add default domain_separator method, pre-compute separator hash * move derive-eip712 deps to dev deps * remove invalid sign payload parameter, add await on async method * remove deprecated comment * debugging 'bad key handle' error for ledger signer try using 'sign_message' * await sign digest for aws signer * remove extra space, fix fmt warning * fix test, fmt errors * use gt 0.6.0 pragma compiler version * enable ABIEncoderV2 for solidity test contract * chore: make test constructor public Co-authored-by: Sebastian Martinez <me@sebastinez.dev> Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
2021-10-08 15:22:51 +00:00
let r = <[u8; 32]>::try_from(sig.r)
.expect("failed to parse 'r' value from signature into [u8; 32]");
let s = <[u8; 32]>::try_from(sig.s)
.expect("failed to parse 's' value from signature into [u8; 32]");
let v = u8::try_from(sig.v).expect("failed to parse 'v' value from signature into u8");
let domain_separator = contract
.domain_separator()
.call()
.await
.expect("failed to retrieve domain_separator from contract");
let type_hash =
contract.type_hash().call().await.expect("failed to retrieve type_hash from contract");
derive-eip712: initial implementation of eip712 derive macro (#481) * derive-eip712: initial implementation of eip712 derive macro This commit provides an initial implementation for a derive macro to encode typed data according to EIP-712, https://eips.ethereum.org/EIPS/eip-712 Additionally, this commit introduces a new signer trait method: async fn sign_typed_data<T: Eip712 + Send + Sync>( &self, payload: &T, ) -> Result<Signature, Self::Error>; And implements the new method for each of the signers (wallet, ledger, aws). Additionally, these changes include using `WalletError` for the Wallet signer error type At the moment, derive does not recurse the primary type to find nested Eip712 structs. This is something that is noted in the source and currently responds with an error regarding custom types. A subsequent PR should be opened once this issue becomes needed. For the moment, the current implementation should satisfy non-nested, basic struct types. * rename to ethers-derive-eip712; move to ethers-core * refactor of derive-eip712 macro; use ParamType and EthAbiToken * macro updates; add byte array checker for paramtype; use literal constant for domain type hash * replace std::convert::Infallible with WalletError as Wallet signer error type * update workspace members and dev dependencies for examples folder * add example for eip712 and test against contract * remove extraneous backward slash in '\x19\x01' prefix; example tests pass * update unreleased change log * remove print statements * use parse_macro_input macro; remove dead code; handle nest struct not implemented error * move eip712 example to solidity-contract tests folder; update cargo workspace dependencies * allow optional EIP712Domain parameter when encoding eip712 struct and signing typed data * add documentation for eip712 feature * Update ethers-signers/src/ledger/mod.rs Co-authored-by: Sebastian Martinez <me@sebastinez.dev> * add error enum for Eip712Error; use sign_payload for ledger signer * add EIP712WithDomain type for providing a wrapper around custom setting of the domain * make LedgerWallet sign_payload public * use optional feature gated dependencies for eip712; add default method for encode_eip712 * add default domain_separator method, pre-compute separator hash * move derive-eip712 deps to dev deps * remove invalid sign payload parameter, add await on async method * remove deprecated comment * debugging 'bad key handle' error for ledger signer try using 'sign_message' * await sign digest for aws signer * remove extra space, fix fmt warning * fix test, fmt errors * use gt 0.6.0 pragma compiler version * enable ABIEncoderV2 for solidity test contract * chore: make test constructor public Co-authored-by: Sebastian Martinez <me@sebastinez.dev> Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
2021-10-08 15:22:51 +00:00
let struct_hash = contract
.struct_hash(derived_foo_bar.clone())
.call()
.await
.expect("failed to retrieve struct_hash from contract");
let encoded = contract
.encode_eip_712(derived_foo_bar.clone())
.call()
.await
.expect("failed to retrieve eip712 encoded hash from contract");
let verify = contract
.verify_foo_bar(wallet.address(), derived_foo_bar, r, s, v)
.call()
.await
.expect("failed to verify signed typed data eip712 payload");
assert_eq!(
domain_separator,
foo_bar
.domain()
.expect("failed to return domain_separator from Eip712 implemented struct")
.separator(),
"domain separator does not match contract domain separator!"
);
assert_eq!(
type_hash,
FooBar::type_hash().expect("failed to return type_hash from Eip712 implemented struct"),
"type hash does not match contract struct type hash!"
);
assert_eq!(
struct_hash,
foo_bar
.clone()
.struct_hash()
.expect("failed to return struct_hash from Eip712 implemented struct"),
"struct hash does not match contract struct hash!"
);
assert_eq!(
encoded,
foo_bar
.encode_eip712()
.expect("failed to return domain_separator from Eip712 implemented struct"),
"Encoded value does not match!"
);
assert!(verify, "typed data signature failed!");
derive-eip712: initial implementation of eip712 derive macro (#481) * derive-eip712: initial implementation of eip712 derive macro This commit provides an initial implementation for a derive macro to encode typed data according to EIP-712, https://eips.ethereum.org/EIPS/eip-712 Additionally, this commit introduces a new signer trait method: async fn sign_typed_data<T: Eip712 + Send + Sync>( &self, payload: &T, ) -> Result<Signature, Self::Error>; And implements the new method for each of the signers (wallet, ledger, aws). Additionally, these changes include using `WalletError` for the Wallet signer error type At the moment, derive does not recurse the primary type to find nested Eip712 structs. This is something that is noted in the source and currently responds with an error regarding custom types. A subsequent PR should be opened once this issue becomes needed. For the moment, the current implementation should satisfy non-nested, basic struct types. * rename to ethers-derive-eip712; move to ethers-core * refactor of derive-eip712 macro; use ParamType and EthAbiToken * macro updates; add byte array checker for paramtype; use literal constant for domain type hash * replace std::convert::Infallible with WalletError as Wallet signer error type * update workspace members and dev dependencies for examples folder * add example for eip712 and test against contract * remove extraneous backward slash in '\x19\x01' prefix; example tests pass * update unreleased change log * remove print statements * use parse_macro_input macro; remove dead code; handle nest struct not implemented error * move eip712 example to solidity-contract tests folder; update cargo workspace dependencies * allow optional EIP712Domain parameter when encoding eip712 struct and signing typed data * add documentation for eip712 feature * Update ethers-signers/src/ledger/mod.rs Co-authored-by: Sebastian Martinez <me@sebastinez.dev> * add error enum for Eip712Error; use sign_payload for ledger signer * add EIP712WithDomain type for providing a wrapper around custom setting of the domain * make LedgerWallet sign_payload public * use optional feature gated dependencies for eip712; add default method for encode_eip712 * add default domain_separator method, pre-compute separator hash * move derive-eip712 deps to dev deps * remove invalid sign payload parameter, add await on async method * remove deprecated comment * debugging 'bad key handle' error for ledger signer try using 'sign_message' * await sign digest for aws signer * remove extra space, fix fmt warning * fix test, fmt errors * use gt 0.6.0 pragma compiler version * enable ABIEncoderV2 for solidity test contract * chore: make test constructor public Co-authored-by: Sebastian Martinez <me@sebastinez.dev> Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
2021-10-08 15:22:51 +00:00
}
}