thegraph_core/signed_message/message.rs
1use alloy::{primitives::Signature, sol_types::SolStruct};
2
3/// EIP-712 signed message
4///
5/// This struct contains a message and the ECDSA signature of the message according to the
6/// EIP-712 standard.
7///
8/// For the message to be signed, it must either:
9/// - To be a _Solidity struct_, i.e., implement the `SolStruct` trait.
10/// - To be convertible into a _Solidity struct_, i.e., implement the `ToSolStruct` trait.
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct SignedMessage<M> {
13 /// Message payload
14 pub message: M,
15 /// ECDSA message signature
16 pub signature: Signature,
17}
18
19impl<M> SignedMessage<M> {
20 /// Get the EIP-712 signature bytes.
21 ///
22 /// The ECDSA signature bytes can be used as a key in a [`BTreeMap`] (or [`HashMap`]) to
23 /// deduplicate signed messages based on their signature.
24 ///
25 /// [`BTreeMap`]: std::collections::BTreeMap
26 /// [`HashMap`]: std::collections::HashMap
27 pub fn signature_bytes(&self) -> SignatureBytes {
28 SignatureBytes(self.signature.as_bytes())
29 }
30
31 /// Hash the message struct according to [EIP-712 `hashStruct`](https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct).
32 ///
33 /// The resulting hash can be used to deduplicate messages. As the hash does not include the
34 /// signature, it is unique for a given message payload. This means that two [`SignedMessage`]s,
35 /// signed by two different signers, will have the same hash.
36 pub fn message_hash<MSol>(&self) -> MessageHash
37 where
38 M: ToSolStruct<MSol>,
39 MSol: SolStruct,
40 {
41 MessageHash(*self.message.to_sol_struct().eip712_hash_struct())
42 }
43}
44
45/// The EIP-712 ECDSA signature bytes.
46///
47/// See: [`SignedMessage::signature_bytes`]
48#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
49pub struct SignatureBytes([u8; 65]);
50
51impl SignatureBytes {
52 /// Get the signature bytes
53 pub fn as_bytes(&self) -> [u8; 65] {
54 self.0
55 }
56}
57
58impl std::ops::Deref for SignatureBytes {
59 type Target = [u8; 65];
60
61 fn deref(&self) -> &Self::Target {
62 &self.0
63 }
64}
65
66/// Message hash according to [EIP-712 `hashStruct`](https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct).
67///
68/// See: [`SignedMessage::message_hash`]
69#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
70pub struct MessageHash([u8; 32]);
71
72impl MessageHash {
73 /// Get the message hash bytes
74 pub fn as_bytes(&self) -> [u8; 32] {
75 self.0
76 }
77}
78
79impl std::ops::Deref for MessageHash {
80 type Target = [u8; 32];
81
82 fn deref(&self) -> &Self::Target {
83 &self.0
84 }
85}
86
87/// A conversion trait for converting a type into a solidity struct representation
88///
89/// This trait is used to convert a Rust type into a struct implementing the `SolStruct` trait.
90pub trait ToSolStruct<T: SolStruct> {
91 /// Convert into the solidity struct representation
92 fn to_sol_struct(&self) -> T;
93}
94
95impl<T> ToSolStruct<T> for T
96where
97 T: SolStruct + Clone,
98{
99 /// Convert into the solidity struct representation.
100 ///
101 /// If the type already implements the `SolStruct` trait, this method will return a clone of
102 /// the type without performing any conversion.
103 fn to_sol_struct(&self) -> T {
104 self.clone()
105 }
106}