Skip to content

Commit 63963a8

Browse files
authored
feat(types): implement order_hash for SignedOrder (#110)
* feat(types): implement `order_hash` for `SignedOrder` wip * chore: do not encode the entire permit, only the actual permit batch and owner * chore: mod docs * chore: keccak intermediate steps * chore: solve nits * chore: add basic test * chore: load order as artifact instead * chore: create very basic signed order manually instead for reproducibility
1 parent 0547177 commit 63963a8

File tree

1 file changed

+81
-2
lines changed

1 file changed

+81
-2
lines changed

crates/types/src/signing/order.rs

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use crate::signing::{permit_signing_info, SignedPermitError, SigningError};
22
use alloy::{
3-
network::TransactionBuilder, primitives::Address, rpc::types::TransactionRequest,
4-
signers::Signer, sol_types::SolCall,
3+
network::TransactionBuilder,
4+
primitives::{keccak256, Address, B256},
5+
rpc::types::TransactionRequest,
6+
signers::Signer,
7+
sol_types::{SolCall, SolValue},
58
};
69
use chrono::Utc;
710
use serde::{Deserialize, Serialize};
@@ -68,6 +71,41 @@ impl SignedOrder {
6871
// construct an initiate tx request
6972
TransactionRequest::default().with_input(initiate_data).with_to(order_contract)
7073
}
74+
75+
/// Get the hash of the order.
76+
///
77+
/// # Composition
78+
///
79+
/// The order hash is composed of the following:
80+
/// - The permit2 batch permit inputs, ABI encoded and hashed.
81+
/// - The permit2 batch owner, ABI encoded and hashed.
82+
/// - The order outputs, ABI encoded and hashed.
83+
/// - The permit2 batch signature, normalized and hashed.
84+
///
85+
/// The components are then hashed together.
86+
pub fn order_hash(&self) -> B256 {
87+
keccak256(self.order_hash_pre_image())
88+
}
89+
90+
/// Get the pre-image for the order hash.
91+
///
92+
/// This is the raw bytes that are hashed to produce the order hash.
93+
#[doc(hidden)]
94+
pub fn order_hash_pre_image(&self) -> Vec<u8> {
95+
// 4 * 32 bytes = 128 bytes
96+
let mut buf = Vec::with_capacity(128);
97+
98+
buf.extend_from_slice(keccak256(self.permit.permit.abi_encode()).as_slice());
99+
buf.extend_from_slice(keccak256(self.permit.owner.abi_encode()).as_slice());
100+
buf.extend_from_slice(keccak256(self.outputs.abi_encode()).as_slice());
101+
102+
// Normalize the signature.
103+
let signature =
104+
alloy::primitives::Signature::from_raw(&self.permit.signature).unwrap().normalized_s();
105+
buf.extend_from_slice(keccak256(signature.as_bytes()).as_slice());
106+
107+
buf
108+
}
71109
}
72110

73111
/// An UnsignedOrder is a helper type used to easily transform an Order into a SignedOrder with correct permit2 semantics.
@@ -147,3 +185,44 @@ impl<'a> UnsignedOrder<'a> {
147185
})
148186
}
149187
}
188+
189+
#[cfg(test)]
190+
mod tests {
191+
use alloy::primitives::{b256, Signature, U256};
192+
use signet_zenith::HostOrders::{PermitBatchTransferFrom, TokenPermissions};
193+
194+
use super::*;
195+
196+
fn basic_order() -> SignedOrder {
197+
SignedOrder::new(
198+
Permit2Batch {
199+
permit: PermitBatchTransferFrom {
200+
permitted: vec![TokenPermissions { token: Address::ZERO, amount: U256::ZERO }],
201+
nonce: U256::ZERO,
202+
deadline: U256::ZERO,
203+
},
204+
owner: Address::ZERO,
205+
signature: Signature::test_signature().as_bytes().into(),
206+
},
207+
vec![Output {
208+
token: Address::ZERO,
209+
amount: U256::ZERO,
210+
recipient: Address::ZERO,
211+
chainId: 0,
212+
}],
213+
)
214+
}
215+
216+
#[test]
217+
fn test_order_hash() {
218+
let order = basic_order();
219+
let hash = order.order_hash();
220+
let pre_image = order.order_hash_pre_image();
221+
222+
assert_eq!(hash, keccak256(pre_image));
223+
assert_eq!(
224+
hash,
225+
b256!("0xba359dd4f891bed0a2cf87c306e59fb6ee099e02b5b0fa86584cdcc44bf6c272")
226+
);
227+
}
228+
}

0 commit comments

Comments
 (0)