-
Notifications
You must be signed in to change notification settings - Fork 79
feat: EIP712 implementation #197
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
41 commits
Select commit
Hold shift + click to select a range
e24b5a2
wip: eip712
developeruche 78dcb10
mod: init draft
developeruche 6d5a3db
fix: fields is FixedBytes
bidzyyys 5a62a97
wip
developeruche 2f4e3f2
wip: abi.encode
developeruche 667f2e5
feat: ABI encode domain separator tuple
bidzyyys 13ce911
mod: eip712 logic implemenation (test in progress)
developeruche 97761cd
mod: added test
developeruche 06140cd
mod: added test
developeruche ce14f87
mod: hard coded chain-id opcode
developeruche 78a1f81
fix: proper chainid shim
bidzyyys 0e26640
feat: add contract_address shim
bidzyyys 318270b
Merge remote-tracking branch 'origin/main' into feat/eip712
alexfertel a0598c5
ref(docs): adjust and simplify doc comments
alexfertel 9093ba6
Merge branch 'main' into feat/eip712
bidzyyys 60afe30
mod: resolved first batch comments
developeruche 7727120
mod: added comment to test
developeruche a59f1bd
mod: port message_hash_utils to lib/crypto
developeruche 9f31c04
mod: hold bug
developeruche 8d0954f
mod: shim update
developeruche 2279ebc
Merge branch 'main' of https://github.com/developeruche/rust-contract…
developeruche ccd0068
mod: introduced EIP712 domain seperator trait
developeruche 34b9351
mod: introduced internal function
developeruche 4f4b115
mod: fmt
developeruche 36ca3a5
ref: refactor EIP-712 interface
bidzyyys e496944
chore: rename files
bidzyyys be10709
feat: add example with EIP-712
bidzyyys 191d840
feat: impl IEIP721 trait for unit tests
bidzyyys f8e9004
fix: CI build
bidzyyys 0572e8c
chore: remove cryptography example
bidzyyys 75565f1
Merge branch 'main' into feat/eip712
bidzyyys 6da2b93
test: add test for eip712_domain
bidzyyys 4b8b552
fix: proper implementation of ERC-191
bidzyyys 32516ad
ref: use keccak256 from crypto lib
bidzyyys ec8da2a
ref: get rid of hex macro in crypto lib
bidzyyys 1c37d3b
ref: get rid of alloy_primitives
bidzyyys 53c2291
ref: store EIP-191 prefix as bytes
bidzyyys 87523e3
ref: use const keccak for const values
bidzyyys 6548111
ref: move implementation back into contracts
alexfertel 58c1c70
ref: remove ieip712.rs file
alexfertel 663f3ff
Merge branch 'main' into feat/eip712
bidzyyys File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| //! [EIP-712](https://eips.ethereum.org/EIPS/eip-712) is a standard for hashing | ||
| //! and signing typed structured data. | ||
| //! | ||
| //! The implementation of the domain separator was designed to be as efficient | ||
| //! as possible while still properly updating the chain id to protect against | ||
| //! replay attacks on an eventual fork of the chain. | ||
| //! | ||
| //! NOTE: This contract implements the version of the encoding known as "v4", as | ||
| //! implemented by the JSON RPC method [`eth_signTypedDataV4`] in `MetaMask`. | ||
| //! | ||
| //! [`eth_signTypedDataV4`]: https://docs.metamask.io/guide/signing-data.html | ||
|
|
||
| use alloc::{borrow::ToOwned, string::String, vec::Vec}; | ||
|
|
||
| use alloy_primitives::{keccak256, Address, FixedBytes, U256}; | ||
| use alloy_sol_types::{sol, SolType}; | ||
|
|
||
| use crate::utils::cryptography::message_hash_utils::to_typed_data_hash; | ||
|
|
||
| /// keccak256("EIP712Domain(string name,string version,uint256 chainId,address | ||
| /// verifyingContract)") | ||
| pub const TYPE_HASH: [u8; 32] = [ | ||
| 0x8b, 0x73, 0xc3, 0xc6, 0x9b, 0xb8, 0xfe, 0x3d, 0x51, 0x2e, 0xcc, 0x4c, | ||
| 0xf7, 0x59, 0xcc, 0x79, 0x23, 0x9f, 0x7b, 0x17, 0x9b, 0x0f, 0xfa, 0xca, | ||
| 0xa9, 0xa7, 0x5d, 0x52, 0x2b, 0x39, 0x40, 0x0f, | ||
| ]; | ||
|
|
||
| /// Field for the domain separator. | ||
| pub const FIELDS: [u8; 1] = [0x0f]; | ||
|
|
||
| /// Salt for the domain separator. | ||
| pub const SALT: [u8; 32] = [0u8; 32]; | ||
|
|
||
| /// Tuple for the domain separator. | ||
| pub type DomainSeparatorTuple = sol! { | ||
| tuple(bytes32, bytes32, bytes32, uint256, address) | ||
| }; | ||
|
|
||
| /// EIP-712 Contract interface. | ||
| pub trait IEIP712 { | ||
| /// Immutable name of EIP-712 instance. | ||
| const NAME: &'static str; | ||
| /// Hashed name of EIP-712 instance. | ||
| const HASHED_NAME: [u8; 32] = | ||
| keccak_const::Keccak256::new().update(Self::NAME.as_bytes()).finalize(); | ||
|
|
||
| /// Immutable version of EIP-712 instance. | ||
| const VERSION: &'static str; | ||
| /// Hashed version of EIP-712 instance. | ||
| const HASHED_VERSION: [u8; 32] = keccak_const::Keccak256::new() | ||
| .update(Self::VERSION.as_bytes()) | ||
| .finalize(); | ||
|
|
||
| /// Returns chain id. | ||
| fn chain_id() -> U256; | ||
| /// Returns the contract's address. | ||
| fn contract_address() -> Address; | ||
|
|
||
| /// Returns the fields and values that describe the domain separator used by | ||
| /// this contract for EIP-712 signature. | ||
| /// | ||
| /// # Arguments | ||
| /// | ||
| /// * `&self` - Read access to the contract's state. | ||
| fn eip712_domain( | ||
| &self, | ||
| ) -> ([u8; 1], String, String, U256, Address, [u8; 32], Vec<U256>) { | ||
| ( | ||
| FIELDS, | ||
| Self::NAME.to_owned(), | ||
| Self::VERSION.to_owned(), | ||
| Self::chain_id(), | ||
| Self::contract_address(), | ||
| SALT, | ||
| Vec::new(), | ||
| ) | ||
| } | ||
|
|
||
| /// Returns the domain separator for the current chain. | ||
| /// | ||
| /// # Arguments | ||
| /// | ||
| /// * `&self` - Read access to the contract's state. | ||
| fn domain_separator_v4(&self) -> FixedBytes<32> { | ||
| let encoded = DomainSeparatorTuple::encode(&( | ||
| TYPE_HASH, | ||
| Self::HASHED_NAME, | ||
| Self::HASHED_VERSION, | ||
| Self::chain_id(), | ||
| Self::contract_address().into(), | ||
| )); | ||
|
|
||
| keccak256(encoded) | ||
| } | ||
|
|
||
| /// Given an already [hashed struct], this function returns the hash of the | ||
| /// fully encoded EIP-712 message for this domain. | ||
| /// | ||
| /// [hashed struct]: https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct | ||
| /// | ||
| /// # Arguments | ||
| /// | ||
| /// * `&self` - Read access to the contract's state. | ||
| fn hash_typed_data_v4( | ||
| &self, | ||
| struct_hash: FixedBytes<32>, | ||
| ) -> FixedBytes<32> { | ||
| let domain_separator = self.domain_separator_v4(); | ||
| to_typed_data_hash(&domain_separator, &struct_hash) | ||
| } | ||
| } | ||
|
|
||
| #[cfg(all(test, feature = "std"))] | ||
| mod tests { | ||
| use alloy_primitives::{address, uint, Address, U256}; | ||
|
|
||
| use super::{FIELDS, IEIP712, SALT}; | ||
|
|
||
| const CHAIN_ID: U256 = uint!(42161_U256); | ||
|
|
||
| const CONTRACT_ADDRESS: Address = | ||
| address!("000000000000000000000000000000000000dEaD"); | ||
|
|
||
| #[derive(Default)] | ||
| struct TestEIP712 {} | ||
|
|
||
| impl IEIP712 for TestEIP712 { | ||
| const NAME: &'static str = "A Name"; | ||
| const VERSION: &'static str = "1"; | ||
|
|
||
| fn chain_id() -> U256 { | ||
| CHAIN_ID | ||
| } | ||
|
|
||
| fn contract_address() -> Address { | ||
| CONTRACT_ADDRESS | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn domain_test() { | ||
| let contract = TestEIP712::default(); | ||
| let domain = contract.eip712_domain(); | ||
| assert_eq!(FIELDS, domain.0); | ||
| assert_eq!(TestEIP712::NAME, domain.1); | ||
| assert_eq!(TestEIP712::VERSION, domain.2); | ||
| assert_eq!(CHAIN_ID, domain.3); | ||
| assert_eq!(CONTRACT_ADDRESS, domain.4); | ||
| assert_eq!(SALT, domain.5); | ||
| assert_eq!(Vec::<U256>::new(), domain.6); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| //! Signature message hash utilities for producing digests to be consumed by | ||
| //! `ECDSA` recovery or signing. | ||
| //! | ||
| //! The library provides methods for generating a hash of a message that | ||
| //! conforms to the [EIP 712] specification. | ||
| //! | ||
| //! [EIP-712]: https://eips.ethereum.org/EIPS/eip-712 | ||
|
|
||
| use alloy_primitives::{keccak256, FixedBytes}; | ||
|
|
||
| /// Prefix for ERC-191 version with `0x01`. | ||
| pub const TYPED_DATA_PREFIX: [u8; 2] = [0x19, 0x01]; | ||
|
|
||
| /// Returns the keccak256 digest of an EIP-712 typed data (ERC-191 version | ||
| /// `0x01`). | ||
| /// | ||
| /// The digest is calculated from a `domain_separator` and a `struct_hash`, by | ||
| /// prefixing them with `[TYPED_DATA_PREFIX]` and hashing the result. It | ||
| /// corresponds to the hash signed by the [eth_signTypedData] JSON-RPC method as | ||
| /// part of EIP-712. | ||
| /// | ||
| /// [eth_signTypedData]: https://eips.ethereum.org/EIPS/eip-712 | ||
| #[must_use] | ||
| pub fn to_typed_data_hash( | ||
| domain_separator: &[u8; 32], | ||
| struct_hash: &[u8; 32], | ||
| ) -> FixedBytes<32> { | ||
| let mut preimage = [0u8; 66]; | ||
| preimage[..2].copy_from_slice(&TYPED_DATA_PREFIX); | ||
| preimage[2..34].copy_from_slice(domain_separator); | ||
| preimage[34..].copy_from_slice(struct_hash); | ||
| keccak256(preimage) | ||
| } | ||
|
|
||
| #[cfg(all(test, feature = "std"))] | ||
| mod tests { | ||
| use alloy_primitives::b256; | ||
|
|
||
| use super::to_typed_data_hash; | ||
|
|
||
| #[test] | ||
| fn test_to_typed_data_hash() { | ||
| // TYPE_HASH | ||
| let domain_separator = b256!( | ||
| "8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f" | ||
| ); | ||
| // bytes32("stylus"); | ||
| let struct_hash = b256!( | ||
| "7379746c75730000000000000000000000000000000000000000000000000000" | ||
| ); | ||
| let expected = b256!( | ||
| "cefc47137f8165d8270433dd62e395f5672966b83a113a7bb7b2805730a2197e" | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| expected, | ||
| to_typed_data_hash(&domain_separator, &struct_hash), | ||
| ); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,4 @@ | ||
| //! Smart Contracts with cryptography. | ||
| pub mod ecdsa; | ||
| pub mod eip712; | ||
| pub mod message_hash_utils; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.