|
1 | 1 | use crate::signing::{permit_signing_info, SignedPermitError, SigningError}; |
2 | 2 | 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}, |
5 | 8 | }; |
6 | 9 | use chrono::Utc; |
7 | 10 | use serde::{Deserialize, Serialize}; |
@@ -68,6 +71,41 @@ impl SignedOrder { |
68 | 71 | // construct an initiate tx request |
69 | 72 | TransactionRequest::default().with_input(initiate_data).with_to(order_contract) |
70 | 73 | } |
| 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 | + } |
71 | 109 | } |
72 | 110 |
|
73 | 111 | /// 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> { |
147 | 185 | }) |
148 | 186 | } |
149 | 187 | } |
| 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