use std::str::FromStr;
use serde::Serialize;
use zksync_basic_types::{web3::keccak256, Address, H256, U256};
use crate::{
ecdsa_signature::K256PrivateKey,
eip712_signature::{
struct_builder::StructBuilder,
typed_structure::{EIP712TypedStructure, Eip712Domain},
utils::get_eip712_json,
},
PackedEthSignature,
};
#[derive(Clone, Serialize)]
struct Person {
name: String,
wallet: Address,
}
impl EIP712TypedStructure for Person {
const TYPE_NAME: &'static str = "Person";
fn build_structure<BUILDER: StructBuilder>(&self, builder: &mut BUILDER) {
builder.add_member("name", &self.name);
builder.add_member("wallet", &self.wallet);
}
}
#[derive(Clone, Serialize)]
struct Mail {
from: Person,
to: Person,
contents: String,
}
impl EIP712TypedStructure for Mail {
const TYPE_NAME: &'static str = "Mail";
fn build_structure<BUILDER: StructBuilder>(&self, builder: &mut BUILDER) {
builder.add_member("from", &self.from);
builder.add_member("to", &self.to);
builder.add_member("contents", &self.contents);
}
}
#[test]
fn test_encode_eip712_typed_struct() {
let domain = Eip712Domain {
name: "Ether Mail".to_owned(),
version: "1".to_owned(),
chain_id: U256::from(1u8),
};
let message = Mail {
from: Person {
name: "Cow".to_owned(),
wallet: Address::from_str("CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826").unwrap(),
},
to: Person {
name: "Bob".to_owned(),
wallet: Address::from_str("bBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB").unwrap(),
},
contents: "Hello, Bob!".to_string(),
};
assert_eq!(
&message.encode_type(),
"Mail(Person from,Person to,string contents)Person(string name,address wallet)"
);
assert_eq!(
&message.encode_data()[..],
[
H256::from_str("fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8")
.unwrap(),
H256::from_str("cd54f074a4af31b4411ff6a60c9719dbd559c221c8ac3492d9d872b041d703d1")
.unwrap(),
H256::from_str("b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8")
.unwrap()
]
);
assert_eq!(
message.hash_struct(),
H256::from_str("c52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e").unwrap()
);
assert_eq!(
&domain.encode_type(),
"EIP712Domain(string name,string version,uint256 chainId)"
);
assert_eq!(
&domain.encode_data()[..],
[
H256::from_str("c70ef06638535b4881fafcac8287e210e3769ff1a8e91f1b95d6246e61e4d3c6")
.unwrap(),
H256::from_str("c89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6")
.unwrap(),
H256::from_str("0000000000000000000000000000000000000000000000000000000000000001")
.unwrap(),
]
);
assert_eq!(
domain.hash_struct(),
H256::from_str("3b98b16ad068d9d8854a6a416bd476de44a4933ec5104d7c786a422ab262ed14").unwrap()
);
let private_key = K256PrivateKey::from_bytes(H256(keccak256(b"cow"))).unwrap();
let address_owner = private_key.address();
let signature = PackedEthSignature::sign_typed_data(&private_key, &domain, &message).unwrap();
let signed_bytes = PackedEthSignature::typed_data_to_signed_bytes(&domain, &message);
assert_eq!(
address_owner,
signature.signature_recover_signer(&signed_bytes).unwrap()
);
}
#[test]
fn test_get_eip712_json() {
let domain = Eip712Domain {
name: "Ether Mail".to_owned(),
version: "1".to_owned(),
chain_id: U256::from(1u8),
};
let message = Mail {
from: Person {
name: "Cow".to_owned(),
wallet: Address::from_str("d94e3dc39d4cad1dad634e7eb585a57a19dc7efe").unwrap(),
},
to: Person {
name: "Bob".to_owned(),
wallet: Address::from_str("d94e3dc39d4cad1dad634e7eb585a57a19dc7efe").unwrap(),
},
contents: "Hello, Bob!".to_string(),
};
let expected_value = r#"{
"domain":{
"chainId":"0x1",
"name":"Ether Mail",
"version":"1"
},
"message":{
"contents":"Hello, Bob!",
"from":{
"name":"Cow",
"wallet":"0xd94e3dc39d4cad1dad634e7eb585a57a19dc7efe"
},
"to":{
"name":"Bob",
"wallet":"0xd94e3dc39d4cad1dad634e7eb585a57a19dc7efe"
}
},
"primaryType":"Mail",
"types":{
"EIP712Domain":[
{
"name":"name",
"type":"string"
},
{
"name":"version",
"type":"string"
},
{
"name":"chainId",
"type":"uint256"
}
],
"Mail":[
{
"name":"from",
"type":"Person"
},
{
"name":"to",
"type":"Person"
},
{
"name":"contents",
"type":"string"
}
],
"Person":[
{
"name":"name",
"type":"string"
},
{
"name":"wallet",
"type":"address"
}
]
}
}"#;
assert_eq!(
get_eip712_json(&domain, &message),
serde_json::from_str::<serde_json::Value>(expected_value).unwrap()
);
}