mod order;
pub use order::{SignedOrder, UnsignedOrder};
mod fill;
pub use fill::{SignedFill, UnsignedFill};
mod error;
pub use error::{SignedPermitError, SigningError};
use alloy::primitives::{Address, B256, U256};
use alloy::sol_types::{Eip712Domain, SolStruct};
use signet_zenith::{
RollupOrders::{
Output, PermitBatchTransferFrom, PermitBatchWitnessTransferFrom, TokenPermissions,
},
PERMIT2_ADDRESS,
};
const PERMIT2_CONTRACT_NAME: &str = "Permit2";
pub(crate) struct PermitSigningInfo {
pub outputs: Vec<Output>,
pub signing_hash: B256,
pub permit: PermitBatchTransferFrom,
}
pub(crate) fn permit_signing_info(
outputs: Vec<Output>,
permitted: Vec<TokenPermissions>,
deadline: u64,
permit2_nonce: u64,
chain_id: u64,
order_contract: Address,
) -> PermitSigningInfo {
let permit_batch = PermitBatchWitnessTransferFrom {
permitted,
spender: order_contract,
nonce: U256::from(permit2_nonce),
deadline: U256::from(deadline),
outputs,
};
let domain = Eip712Domain {
chain_id: Some(U256::from(chain_id)),
name: Some(PERMIT2_CONTRACT_NAME.into()),
verifying_contract: Some(PERMIT2_ADDRESS),
version: None,
salt: None,
};
let signing_hash = permit_batch.eip712_signing_hash(&domain);
let permit = PermitBatchTransferFrom {
permitted: permit_batch.permitted,
nonce: U256::from(permit2_nonce),
deadline: U256::from(deadline),
};
PermitSigningInfo { outputs: permit_batch.outputs, signing_hash, permit }
}
#[cfg(test)]
mod tests {
use super::*;
use alloy::primitives::{address, b256};
use alloy::sol_types::SolStruct;
#[derive(Debug, serde::Serialize)]
struct Eip712TestVector {
name: &'static str,
chain_id: u64,
order_contract: String,
permitted: Vec<PermittedJson>,
nonce: String,
deadline: String,
outputs: Vec<OutputJson>,
expected_domain_separator: String,
expected_struct_hash: String,
expected_signing_hash: String,
}
#[derive(Debug, serde::Serialize)]
struct PermittedJson {
token: String,
amount: String,
}
#[derive(Debug, serde::Serialize)]
struct OutputJson {
token: String,
amount: String,
recipient: String,
chain_id: u32,
}
fn build_eip712_test_vectors() -> Vec<Eip712TestVector> {
vec![
{
let permitted = vec![TokenPermissions { token: Address::ZERO, amount: U256::ZERO }];
let outputs = vec![Output {
token: Address::ZERO,
amount: U256::ZERO,
recipient: Address::ZERO,
chainId: 0,
}];
let chain_id = 1u64;
let order_contract = Address::ZERO;
let nonce = 0u64;
let deadline = 0u64;
let permit_batch = PermitBatchWitnessTransferFrom {
permitted: permitted.clone(),
spender: order_contract,
nonce: U256::from(nonce),
deadline: U256::from(deadline),
outputs: outputs.clone(),
};
let domain = Eip712Domain {
chain_id: Some(U256::from(chain_id)),
name: Some(PERMIT2_CONTRACT_NAME.into()),
verifying_contract: Some(PERMIT2_ADDRESS),
version: None,
salt: None,
};
let domain_separator = domain.hash_struct();
let struct_hash = permit_batch.eip712_hash_struct();
let signing_hash = permit_batch.eip712_signing_hash(&domain);
Eip712TestVector {
name: "minimal",
chain_id,
order_contract: format!("{:#x}", order_contract),
permitted: permitted
.iter()
.map(|p| PermittedJson {
token: format!("{:#x}", p.token),
amount: format!("{:#x}", p.amount),
})
.collect(),
nonce: format!("{:#x}", U256::from(nonce)),
deadline: format!("{:#x}", U256::from(deadline)),
outputs: outputs
.iter()
.map(|o| OutputJson {
token: format!("{:#x}", o.token),
amount: format!("{:#x}", o.amount),
recipient: format!("{:#x}", o.recipient),
chain_id: o.chainId,
})
.collect(),
expected_domain_separator: format!("{:#x}", domain_separator),
expected_struct_hash: format!("{:#x}", struct_hash),
expected_signing_hash: format!("{:#x}", signing_hash),
}
},
{
let usdc = address!("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
let recipient = address!("0x1234567890123456789012345678901234567890");
let order_contract = address!("0x96f44ddc3Bc8892371305531F1a6d8ca2331fE6C");
let permitted =
vec![TokenPermissions { token: usdc, amount: U256::from(1_000_000u64) }];
let outputs = vec![Output {
token: Address::ZERO,
amount: U256::from(500_000_000_000_000_000u64),
recipient,
chainId: 1,
}];
let chain_id = 1u64;
let nonce = 12345u64;
let deadline = 1700000000u64;
let permit_batch = PermitBatchWitnessTransferFrom {
permitted: permitted.clone(),
spender: order_contract,
nonce: U256::from(nonce),
deadline: U256::from(deadline),
outputs: outputs.clone(),
};
let domain = Eip712Domain {
chain_id: Some(U256::from(chain_id)),
name: Some(PERMIT2_CONTRACT_NAME.into()),
verifying_contract: Some(PERMIT2_ADDRESS),
version: None,
salt: None,
};
let domain_separator = domain.hash_struct();
let struct_hash = permit_batch.eip712_hash_struct();
let signing_hash = permit_batch.eip712_signing_hash(&domain);
Eip712TestVector {
name: "mainnet_usdc_swap",
chain_id,
order_contract: format!("{:#x}", order_contract),
permitted: permitted
.iter()
.map(|p| PermittedJson {
token: format!("{:#x}", p.token),
amount: format!("{:#x}", p.amount),
})
.collect(),
nonce: format!("{:#x}", U256::from(nonce)),
deadline: format!("{:#x}", U256::from(deadline)),
outputs: outputs
.iter()
.map(|o| OutputJson {
token: format!("{:#x}", o.token),
amount: format!("{:#x}", o.amount),
recipient: format!("{:#x}", o.recipient),
chain_id: o.chainId,
})
.collect(),
expected_domain_separator: format!("{:#x}", domain_separator),
expected_struct_hash: format!("{:#x}", struct_hash),
expected_signing_hash: format!("{:#x}", signing_hash),
}
},
{
let usdc = address!("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
let recipient_a = address!("0x1111111111111111111111111111111111111111");
let recipient_b = address!("0x2222222222222222222222222222222222222222");
let order_contract = address!("0x000000000000007369676E65742D6f7264657273");
let permitted =
vec![TokenPermissions { token: usdc, amount: U256::from(5_000_000u64) }];
let outputs = vec![
Output {
token: usdc,
amount: U256::from(2_500_000u64),
recipient: recipient_a,
chainId: 1,
},
Output {
token: usdc,
amount: U256::from(2_500_000u64),
recipient: recipient_b,
chainId: 519,
},
];
let chain_id = 519u64; let nonce = 999999u64;
let deadline = 1800000000u64;
let permit_batch = PermitBatchWitnessTransferFrom {
permitted: permitted.clone(),
spender: order_contract,
nonce: U256::from(nonce),
deadline: U256::from(deadline),
outputs: outputs.clone(),
};
let domain = Eip712Domain {
chain_id: Some(U256::from(chain_id)),
name: Some(PERMIT2_CONTRACT_NAME.into()),
verifying_contract: Some(PERMIT2_ADDRESS),
version: None,
salt: None,
};
let domain_separator = domain.hash_struct();
let struct_hash = permit_batch.eip712_hash_struct();
let signing_hash = permit_batch.eip712_signing_hash(&domain);
Eip712TestVector {
name: "rollup_cross_chain",
chain_id,
order_contract: format!("{:#x}", order_contract),
permitted: permitted
.iter()
.map(|p| PermittedJson {
token: format!("{:#x}", p.token),
amount: format!("{:#x}", p.amount),
})
.collect(),
nonce: format!("{:#x}", U256::from(nonce)),
deadline: format!("{:#x}", U256::from(deadline)),
outputs: outputs
.iter()
.map(|o| OutputJson {
token: format!("{:#x}", o.token),
amount: format!("{:#x}", o.amount),
recipient: format!("{:#x}", o.recipient),
chain_id: o.chainId,
})
.collect(),
expected_domain_separator: format!("{:#x}", domain_separator),
expected_struct_hash: format!("{:#x}", struct_hash),
expected_signing_hash: format!("{:#x}", signing_hash),
}
},
{
let weth = address!("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
let recipient = address!("0x5555555555555555555555555555555555555555");
let order_contract = address!("0x96f44ddc3Bc8892371305531F1a6d8ca2331fE6C");
let large_amount = U256::from(10_000_000_000_000_000_000_000u128);
let permitted = vec![TokenPermissions { token: weth, amount: large_amount }];
let outputs =
vec![Output { token: weth, amount: large_amount, recipient, chainId: 1 }];
let chain_id = 1u64;
let nonce = u64::MAX;
let deadline = u64::MAX;
let permit_batch = PermitBatchWitnessTransferFrom {
permitted: permitted.clone(),
spender: order_contract,
nonce: U256::from(nonce),
deadline: U256::from(deadline),
outputs: outputs.clone(),
};
let domain = Eip712Domain {
chain_id: Some(U256::from(chain_id)),
name: Some(PERMIT2_CONTRACT_NAME.into()),
verifying_contract: Some(PERMIT2_ADDRESS),
version: None,
salt: None,
};
let domain_separator = domain.hash_struct();
let struct_hash = permit_batch.eip712_hash_struct();
let signing_hash = permit_batch.eip712_signing_hash(&domain);
Eip712TestVector {
name: "large_amounts",
chain_id,
order_contract: format!("{:#x}", order_contract),
permitted: permitted
.iter()
.map(|p| PermittedJson {
token: format!("{:#x}", p.token),
amount: format!("{:#x}", p.amount),
})
.collect(),
nonce: format!("{:#x}", U256::from(nonce)),
deadline: format!("{:#x}", U256::from(deadline)),
outputs: outputs
.iter()
.map(|o| OutputJson {
token: format!("{:#x}", o.token),
amount: format!("{:#x}", o.amount),
recipient: format!("{:#x}", o.recipient),
chain_id: o.chainId,
})
.collect(),
expected_domain_separator: format!("{:#x}", domain_separator),
expected_struct_hash: format!("{:#x}", struct_hash),
expected_signing_hash: format!("{:#x}", signing_hash),
}
},
]
}
#[test]
#[ignore = "vector generation for external SDK - run manually when needed"]
fn eip712_signing_vectors() {
let vectors = build_eip712_test_vectors();
println!("\n=== EIP-712 SIGNING HASH TEST VECTORS ===\n");
println!("// Generated for TypeScript SDK verification");
println!("// Copy this JSON array to tests/eip712-vectors.json\n");
let output = serde_json::to_string_pretty(&vectors).unwrap();
println!("{}", output);
println!("\n=== END TEST VECTORS ===\n");
}
#[test]
fn minimal_eip712_signing_hash() {
let permitted = vec![TokenPermissions { token: Address::ZERO, amount: U256::ZERO }];
let outputs = vec![Output {
token: Address::ZERO,
amount: U256::ZERO,
recipient: Address::ZERO,
chainId: 0,
}];
let permit_batch = PermitBatchWitnessTransferFrom {
permitted,
spender: Address::ZERO,
nonce: U256::ZERO,
deadline: U256::ZERO,
outputs,
};
let domain = Eip712Domain {
chain_id: Some(U256::from(1u64)),
name: Some(PERMIT2_CONTRACT_NAME.into()),
verifying_contract: Some(PERMIT2_ADDRESS),
version: None,
salt: None,
};
let signing_hash = permit_batch.eip712_signing_hash(&domain);
assert_eq!(
signing_hash,
b256!("0x4c8eb855427d98f29425e966e3d7526f9ed6b787a23895d12069f939fd21cc07")
);
}
#[test]
fn permit2_domain_separator() {
let domain = Eip712Domain {
chain_id: Some(U256::from(1u64)),
name: Some(PERMIT2_CONTRACT_NAME.into()),
verifying_contract: Some(PERMIT2_ADDRESS),
version: None,
salt: None,
};
let domain_separator = domain.hash_struct();
println!("Domain separator: {:#x}", domain_separator);
assert_eq!(
domain_separator,
b256!("0x866a5aba21966af95d6c7ab78eb2b2fc913915c28be3b9aa07cc04ff903e3f28")
);
}
fn build_fill_test_vectors() -> Vec<Eip712TestVector> {
vec![
{
let permitted = vec![TokenPermissions { token: Address::ZERO, amount: U256::ZERO }];
let outputs = vec![Output {
token: Address::ZERO,
amount: U256::ZERO,
recipient: Address::ZERO,
chainId: 1,
}];
let chain_id = 1u64;
let order_contract = Address::ZERO;
let nonce = 0u64;
let deadline = 0u64;
let permit_batch = PermitBatchWitnessTransferFrom {
permitted: permitted.clone(),
spender: order_contract,
nonce: U256::from(nonce),
deadline: U256::from(deadline),
outputs: outputs.clone(),
};
let domain = Eip712Domain {
chain_id: Some(U256::from(chain_id)),
name: Some(PERMIT2_CONTRACT_NAME.into()),
verifying_contract: Some(PERMIT2_ADDRESS),
version: None,
salt: None,
};
let domain_separator = domain.hash_struct();
let struct_hash = permit_batch.eip712_hash_struct();
let signing_hash = permit_batch.eip712_signing_hash(&domain);
Eip712TestVector {
name: "minimal_fill",
chain_id,
order_contract: format!("{:#x}", order_contract),
permitted: permitted
.iter()
.map(|p| PermittedJson {
token: format!("{:#x}", p.token),
amount: format!("{:#x}", p.amount),
})
.collect(),
nonce: format!("{:#x}", U256::from(nonce)),
deadline: format!("{:#x}", U256::from(deadline)),
outputs: outputs
.iter()
.map(|o| OutputJson {
token: format!("{:#x}", o.token),
amount: format!("{:#x}", o.amount),
recipient: format!("{:#x}", o.recipient),
chain_id: o.chainId,
})
.collect(),
expected_domain_separator: format!("{:#x}", domain_separator),
expected_struct_hash: format!("{:#x}", struct_hash),
expected_signing_hash: format!("{:#x}", signing_hash),
}
},
{
let weth = address!("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
let recipient = address!("0xABCDABCDABCDABCDABCDABCDABCDABCDABCDABCD");
let order_contract = address!("0x96f44ddc3Bc8892371305531F1a6d8ca2331fE6C");
let fill_amount = U256::from(1_000_000_000_000_000_000u64); let permitted = vec![TokenPermissions { token: weth, amount: fill_amount }];
let outputs =
vec![Output { token: weth, amount: fill_amount, recipient, chainId: 1 }];
let chain_id = 1u64;
let nonce = 1000u64;
let deadline = 1700000000u64;
let permit_batch = PermitBatchWitnessTransferFrom {
permitted: permitted.clone(),
spender: order_contract,
nonce: U256::from(nonce),
deadline: U256::from(deadline),
outputs: outputs.clone(),
};
let domain = Eip712Domain {
chain_id: Some(U256::from(chain_id)),
name: Some(PERMIT2_CONTRACT_NAME.into()),
verifying_contract: Some(PERMIT2_ADDRESS),
version: None,
salt: None,
};
let domain_separator = domain.hash_struct();
let struct_hash = permit_batch.eip712_hash_struct();
let signing_hash = permit_batch.eip712_signing_hash(&domain);
Eip712TestVector {
name: "mainnet_eth_fill",
chain_id,
order_contract: format!("{:#x}", order_contract),
permitted: permitted
.iter()
.map(|p| PermittedJson {
token: format!("{:#x}", p.token),
amount: format!("{:#x}", p.amount),
})
.collect(),
nonce: format!("{:#x}", U256::from(nonce)),
deadline: format!("{:#x}", U256::from(deadline)),
outputs: outputs
.iter()
.map(|o| OutputJson {
token: format!("{:#x}", o.token),
amount: format!("{:#x}", o.amount),
recipient: format!("{:#x}", o.recipient),
chain_id: o.chainId,
})
.collect(),
expected_domain_separator: format!("{:#x}", domain_separator),
expected_struct_hash: format!("{:#x}", struct_hash),
expected_signing_hash: format!("{:#x}", signing_hash),
}
},
{
let weth = address!("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
let usdc = address!("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
let recipient = address!("0x7777777777777777777777777777777777777777");
let order_contract = address!("0x96f44ddc3Bc8892371305531F1a6d8ca2331fE6C");
let weth_amount = U256::from(500_000_000_000_000_000u64); let usdc_amount = U256::from(1_000_000_000u64);
let permitted = vec![
TokenPermissions { token: weth, amount: weth_amount },
TokenPermissions { token: usdc, amount: usdc_amount },
];
let outputs = vec![
Output { token: weth, amount: weth_amount, recipient, chainId: 1 },
Output { token: usdc, amount: usdc_amount, recipient, chainId: 1 },
];
let chain_id = 1u64;
let nonce = 42u64;
let deadline = 1750000000u64;
let permit_batch = PermitBatchWitnessTransferFrom {
permitted: permitted.clone(),
spender: order_contract,
nonce: U256::from(nonce),
deadline: U256::from(deadline),
outputs: outputs.clone(),
};
let domain = Eip712Domain {
chain_id: Some(U256::from(chain_id)),
name: Some(PERMIT2_CONTRACT_NAME.into()),
verifying_contract: Some(PERMIT2_ADDRESS),
version: None,
salt: None,
};
let domain_separator = domain.hash_struct();
let struct_hash = permit_batch.eip712_hash_struct();
let signing_hash = permit_batch.eip712_signing_hash(&domain);
Eip712TestVector {
name: "multi_token_fill",
chain_id,
order_contract: format!("{:#x}", order_contract),
permitted: permitted
.iter()
.map(|p| PermittedJson {
token: format!("{:#x}", p.token),
amount: format!("{:#x}", p.amount),
})
.collect(),
nonce: format!("{:#x}", U256::from(nonce)),
deadline: format!("{:#x}", U256::from(deadline)),
outputs: outputs
.iter()
.map(|o| OutputJson {
token: format!("{:#x}", o.token),
amount: format!("{:#x}", o.amount),
recipient: format!("{:#x}", o.recipient),
chain_id: o.chainId,
})
.collect(),
expected_domain_separator: format!("{:#x}", domain_separator),
expected_struct_hash: format!("{:#x}", struct_hash),
expected_signing_hash: format!("{:#x}", signing_hash),
}
},
{
let usdc = address!("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
let recipient_mainnet = address!("0x3333333333333333333333333333333333333333");
let recipient_rollup = address!("0x4444444444444444444444444444444444444444");
let order_contract = address!("0x96f44ddc3Bc8892371305531F1a6d8ca2331fE6C");
let mainnet_amount = U256::from(500_000_000u64); let rollup_amount = U256::from(500_000_000u64); let total_amount = mainnet_amount + rollup_amount;
let permitted = vec![TokenPermissions { token: usdc, amount: total_amount }];
let outputs = vec![
Output {
token: usdc,
amount: mainnet_amount,
recipient: recipient_mainnet,
chainId: 1,
},
Output {
token: usdc,
amount: rollup_amount,
recipient: recipient_rollup,
chainId: 519,
},
];
let chain_id = 1u64; let nonce = 88888u64;
let deadline = 1800000000u64;
let permit_batch = PermitBatchWitnessTransferFrom {
permitted: permitted.clone(),
spender: order_contract,
nonce: U256::from(nonce),
deadline: U256::from(deadline),
outputs: outputs.clone(),
};
let domain = Eip712Domain {
chain_id: Some(U256::from(chain_id)),
name: Some(PERMIT2_CONTRACT_NAME.into()),
verifying_contract: Some(PERMIT2_ADDRESS),
version: None,
salt: None,
};
let domain_separator = domain.hash_struct();
let struct_hash = permit_batch.eip712_hash_struct();
let signing_hash = permit_batch.eip712_signing_hash(&domain);
Eip712TestVector {
name: "cross_chain_fill",
chain_id,
order_contract: format!("{:#x}", order_contract),
permitted: permitted
.iter()
.map(|p| PermittedJson {
token: format!("{:#x}", p.token),
amount: format!("{:#x}", p.amount),
})
.collect(),
nonce: format!("{:#x}", U256::from(nonce)),
deadline: format!("{:#x}", U256::from(deadline)),
outputs: outputs
.iter()
.map(|o| OutputJson {
token: format!("{:#x}", o.token),
amount: format!("{:#x}", o.amount),
recipient: format!("{:#x}", o.recipient),
chain_id: o.chainId,
})
.collect(),
expected_domain_separator: format!("{:#x}", domain_separator),
expected_struct_hash: format!("{:#x}", struct_hash),
expected_signing_hash: format!("{:#x}", signing_hash),
}
},
{
let weth = address!("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
let recipient = address!("0x9999999999999999999999999999999999999999");
let order_contract = address!("0x000000000000007369676E65742D6f7264657273");
let large_amount = U256::from(50_000_000_000_000_000_000_000u128);
let permitted = vec![TokenPermissions { token: weth, amount: large_amount }];
let outputs =
vec![Output { token: weth, amount: large_amount, recipient, chainId: 1 }];
let chain_id = 519u64; let nonce = u64::MAX;
let deadline = u64::MAX;
let permit_batch = PermitBatchWitnessTransferFrom {
permitted: permitted.clone(),
spender: order_contract,
nonce: U256::from(nonce),
deadline: U256::from(deadline),
outputs: outputs.clone(),
};
let domain = Eip712Domain {
chain_id: Some(U256::from(chain_id)),
name: Some(PERMIT2_CONTRACT_NAME.into()),
verifying_contract: Some(PERMIT2_ADDRESS),
version: None,
salt: None,
};
let domain_separator = domain.hash_struct();
let struct_hash = permit_batch.eip712_hash_struct();
let signing_hash = permit_batch.eip712_signing_hash(&domain);
Eip712TestVector {
name: "large_amount_fill",
chain_id,
order_contract: format!("{:#x}", order_contract),
permitted: permitted
.iter()
.map(|p| PermittedJson {
token: format!("{:#x}", p.token),
amount: format!("{:#x}", p.amount),
})
.collect(),
nonce: format!("{:#x}", U256::from(nonce)),
deadline: format!("{:#x}", U256::from(deadline)),
outputs: outputs
.iter()
.map(|o| OutputJson {
token: format!("{:#x}", o.token),
amount: format!("{:#x}", o.amount),
recipient: format!("{:#x}", o.recipient),
chain_id: o.chainId,
})
.collect(),
expected_domain_separator: format!("{:#x}", domain_separator),
expected_struct_hash: format!("{:#x}", struct_hash),
expected_signing_hash: format!("{:#x}", signing_hash),
}
},
{
let usdc = address!("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
let recipient = address!("0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF");
let order_contract = address!("0x000000000000007369676E65742D6f7264657273");
let fill_amount = U256::from(10_000_000_000u64);
let permitted = vec![TokenPermissions { token: usdc, amount: fill_amount }];
let outputs =
vec![Output { token: usdc, amount: fill_amount, recipient, chainId: 519 }];
let chain_id = 519u64; let nonce = 123456789u64;
let deadline = 1850000000u64;
let permit_batch = PermitBatchWitnessTransferFrom {
permitted: permitted.clone(),
spender: order_contract,
nonce: U256::from(nonce),
deadline: U256::from(deadline),
outputs: outputs.clone(),
};
let domain = Eip712Domain {
chain_id: Some(U256::from(chain_id)),
name: Some(PERMIT2_CONTRACT_NAME.into()),
verifying_contract: Some(PERMIT2_ADDRESS),
version: None,
salt: None,
};
let domain_separator = domain.hash_struct();
let struct_hash = permit_batch.eip712_hash_struct();
let signing_hash = permit_batch.eip712_signing_hash(&domain);
Eip712TestVector {
name: "signet_rollup_fill",
chain_id,
order_contract: format!("{:#x}", order_contract),
permitted: permitted
.iter()
.map(|p| PermittedJson {
token: format!("{:#x}", p.token),
amount: format!("{:#x}", p.amount),
})
.collect(),
nonce: format!("{:#x}", U256::from(nonce)),
deadline: format!("{:#x}", U256::from(deadline)),
outputs: outputs
.iter()
.map(|o| OutputJson {
token: format!("{:#x}", o.token),
amount: format!("{:#x}", o.amount),
recipient: format!("{:#x}", o.recipient),
chain_id: o.chainId,
})
.collect(),
expected_domain_separator: format!("{:#x}", domain_separator),
expected_struct_hash: format!("{:#x}", struct_hash),
expected_signing_hash: format!("{:#x}", signing_hash),
}
},
]
}
#[test]
#[ignore = "vector generation for external SDK - run manually when needed"]
fn fill_signing_vectors() {
let vectors = build_fill_test_vectors();
println!("\n=== FILL EIP-712 SIGNING HASH TEST VECTORS ===\n");
println!("// Generated for TypeScript SDK verification");
println!("// Copy this JSON array to tests/fill-vectors.json\n");
let output = serde_json::to_string_pretty(&vectors).unwrap();
println!("{}", output);
println!("\n=== END TEST VECTORS ===\n");
}
}