use ethers::abi::{encode, Token};
use ethers::signers::LocalWallet;
use ethers::types::{Address, H256};
use ethers::utils::keccak256;
use crate::error::{RelayerError, Result};
pub const EIP1271_MAGIC_VALUE: [u8; 4] = [0x16, 0x26, 0xba, 0x7e];
pub fn sign_hash_for_safe(wallet: &LocalWallet, hash: H256) -> Result<Vec<u8>> {
let sig = wallet
.sign_hash(hash)
.map_err(|e| RelayerError::Signing(e.to_string()))?;
let mut bytes = Vec::with_capacity(65);
let mut r = [0u8; 32];
sig.r.to_big_endian(&mut r);
bytes.extend_from_slice(&r);
let mut s = [0u8; 32];
sig.s.to_big_endian(&mut s);
bytes.extend_from_slice(&s);
let v = sig.v as u8;
let v_final = if v < 27 { v + 27 } else { v };
bytes.push(v_final);
Ok(bytes)
}
pub fn is_valid_signature_calldata(hash: H256, signature: &[u8]) -> Vec<u8> {
let selector = &keccak256(b"isValidSignature(bytes32,bytes)")[..4];
let mut out = selector.to_vec();
out.extend_from_slice(&encode(&[
Token::FixedBytes(hash.as_bytes().to_vec()),
Token::Bytes(signature.to_vec()),
]));
out
}
pub fn is_magic_value(response: &[u8]) -> bool {
response.len() >= 4 && response[..4] == EIP1271_MAGIC_VALUE
}
pub fn safe_prevalidated_signature(owner: Address) -> Vec<u8> {
let mut bytes = Vec::with_capacity(65);
let mut r = [0u8; 32];
r[12..32].copy_from_slice(owner.as_bytes());
bytes.extend_from_slice(&r);
bytes.extend_from_slice(&[0u8; 32]); bytes.push(1u8); bytes
}
#[cfg(test)]
mod tests {
use super::*;
use ethers::signers::Signer;
#[test]
fn magic_value_matches_eip1271() {
let h = keccak256(b"isValidSignature(bytes32,bytes)");
assert_eq!(&h[..4], &EIP1271_MAGIC_VALUE);
}
#[test]
fn is_magic_value_recognises_padded_response() {
let mut padded = [0u8; 32];
padded[..4].copy_from_slice(&EIP1271_MAGIC_VALUE);
assert!(is_magic_value(&padded));
assert!(!is_magic_value(&[0u8; 32]));
}
#[test]
fn prevalidated_signature_layout() {
let owner: Address = "0x000000000000000000000000000000000000abcd"
.parse()
.unwrap();
let sig = safe_prevalidated_signature(owner);
assert_eq!(sig.len(), 65);
assert_eq!(&sig[12..32], owner.as_bytes());
assert_eq!(&sig[32..64], &[0u8; 32]);
assert_eq!(sig[64], 1);
}
#[test]
fn sign_hash_for_safe_produces_65_bytes_with_v27_or_v28() {
let wallet: LocalWallet = "0x0000000000000000000000000000000000000000000000000000000000000001"
.parse()
.unwrap();
let hash = H256::from(keccak256(b"some-order-hash"));
let sig = sign_hash_for_safe(&wallet, hash).unwrap();
assert_eq!(sig.len(), 65);
assert!(matches!(sig[64], 27 | 28));
let _ = wallet.address();
}
}