use alloy_primitives::{keccak256, B256};
use eyre::Result;
#[cfg(feature = "client")]
pub mod rpc;
pub const MIDRIB_EIP712_NAME: &str = "Midrib";
pub const MIDRIB_EIP712_VERSION: &str = "3";
pub fn envelope_signing_digest(message: &[u8]) -> B256 {
let prefix = format!("\x19Ethereum Signed Message:\n{}", message.len());
let mut buf = Vec::with_capacity(prefix.len() + message.len());
buf.extend_from_slice(prefix.as_bytes());
buf.extend_from_slice(message);
keccak256(&buf)
}
pub async fn sign_send_order_envelope(
wallet: &crate::wallet::Wallet,
encoded_order: &[u8],
) -> Result<Vec<u8>> {
wallet.sign_message(encoded_order).await
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn eip712_constants_are_exact() {
assert_eq!(MIDRIB_EIP712_NAME, "Midrib");
assert_eq!(MIDRIB_EIP712_VERSION, "3");
}
#[test]
fn envelope_digest_matches_eip191() {
let digest = envelope_signing_digest(b"");
let expected = keccak256(b"\x19Ethereum Signed Message:\n0");
assert_eq!(digest, expected);
}
#[test]
fn envelope_digest_length_prefix_is_byte_count() {
for msg in [b"a".as_slice(), b"hello", &[0u8; 32], &[0xffu8; 256]] {
let digest = envelope_signing_digest(msg);
let mut buf = format!("\x19Ethereum Signed Message:\n{}", msg.len()).into_bytes();
buf.extend_from_slice(msg);
assert_eq!(digest, keccak256(&buf), "len={}", msg.len());
}
}
#[tokio::test]
async fn sign_send_order_envelope_round_trips_to_signer_address() {
use alloy_primitives::Signature;
use std::str::FromStr;
let wallet = crate::wallet::Wallet::from_evm_hex(
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
)
.unwrap();
let signer_address = alloy_primitives::Address::from_str(&wallet.address()).unwrap();
let encoded_order = b"\x00\x01encoded-send-order-payload";
let sig_bytes = sign_send_order_envelope(&wallet, encoded_order)
.await
.unwrap();
assert_eq!(
sig_bytes.len(),
65,
"sign_send_order_envelope must return r||s||v = 65 bytes; arborter rejects anything else"
);
let sig = Signature::try_from(sig_bytes.as_slice()).unwrap();
let recovered = sig
.recover_address_from_msg(encoded_order.as_slice())
.unwrap();
assert_eq!(
recovered, signer_address,
"recovered address must match the signing wallet — this is exactly what arborter checks"
);
}
}