use alloy_primitives::{Address, B256, U256, keccak256};
use crate::error::CowError;
use super::types::{ConditionalOrderParams, GpV2OrderStruct, TWAP_HANDLER_ADDRESS};
pub const GAT_HANDLER_ADDRESS: Address = TWAP_HANDLER_ADDRESS;
#[derive(Debug, Clone)]
pub struct GatData {
pub order: GpV2OrderStruct,
pub start_time: u32,
pub tx_deadline: u32,
}
#[derive(Debug, Clone)]
pub struct GatOrder {
pub data: GatData,
pub salt: B256,
}
impl GatOrder {
#[must_use]
pub fn new(data: GatData) -> Self {
let salt = deterministic_salt(&data);
Self { data, salt }
}
#[must_use]
pub const fn with_salt(data: GatData, salt: B256) -> Self {
Self { data, salt }
}
#[must_use]
pub fn is_valid(&self) -> bool {
let d = &self.data;
let o = &d.order;
!o.sell_amount.is_zero() &&
!o.buy_amount.is_zero() &&
o.sell_token != o.buy_token &&
d.start_time <= d.tx_deadline
}
pub fn to_params(&self) -> Result<ConditionalOrderParams, CowError> {
Ok(ConditionalOrderParams {
handler: GAT_HANDLER_ADDRESS,
salt: self.salt,
static_input: encode_gat_struct(&self.data),
})
}
#[must_use]
pub const fn data_ref(&self) -> &GatData {
&self.data
}
#[must_use]
pub const fn salt_ref(&self) -> &B256 {
&self.salt
}
}
#[must_use]
pub fn encode_gat_struct(d: &GatData) -> Vec<u8> {
let o = &d.order;
let mut buf = Vec::with_capacity(14 * 32);
buf.extend_from_slice(&pad_address(o.sell_token.as_slice()));
buf.extend_from_slice(&pad_address(o.buy_token.as_slice()));
buf.extend_from_slice(&pad_address(o.receiver.as_slice()));
buf.extend_from_slice(&u256_bytes(o.sell_amount));
buf.extend_from_slice(&u256_bytes(o.buy_amount));
buf.extend_from_slice(&u256_be(u64::from(o.valid_to)));
buf.extend_from_slice(o.app_data.as_slice());
buf.extend_from_slice(&u256_bytes(o.fee_amount));
buf.extend_from_slice(o.kind.as_slice());
buf.extend_from_slice(&bool_word(o.partially_fillable));
buf.extend_from_slice(o.sell_token_balance.as_slice());
buf.extend_from_slice(o.buy_token_balance.as_slice());
buf.extend_from_slice(&u256_be(u64::from(d.start_time)));
buf.extend_from_slice(&u256_be(u64::from(d.tx_deadline)));
buf
}
pub fn decode_gat_static_input(bytes: &[u8]) -> Result<GatData, CowError> {
if bytes.len() < 14 * 32 {
return Err(CowError::AppData(format!(
"GAT static input too short: {} bytes (need 448)",
bytes.len()
)));
}
let addr = |off: usize| -> Address {
let mut a = [0u8; 20];
a.copy_from_slice(&bytes[off + 12..off + 32]);
Address::new(a)
};
let u256 = |off: usize| -> U256 { U256::from_be_slice(&bytes[off..off + 32]) };
let u32v = |off: usize| -> u32 {
u32::from_be_bytes([bytes[off + 28], bytes[off + 29], bytes[off + 30], bytes[off + 31]])
};
let bool_v = |off: usize| -> bool { bytes[off + 31] != 0 };
let b256 = |off: usize| -> B256 {
let mut arr = [0u8; 32];
arr.copy_from_slice(&bytes[off..off + 32]);
B256::new(arr)
};
let order = GpV2OrderStruct {
sell_token: addr(0),
buy_token: addr(32),
receiver: addr(64),
sell_amount: u256(96),
buy_amount: u256(128),
valid_to: u32v(160),
app_data: b256(192),
fee_amount: u256(224),
kind: b256(256),
partially_fillable: bool_v(288),
sell_token_balance: b256(320),
buy_token_balance: b256(352),
};
Ok(GatData { order, start_time: u32v(384), tx_deadline: u32v(416) })
}
fn pad_address(bytes: &[u8]) -> [u8; 32] {
let mut out = [0u8; 32];
out[12..].copy_from_slice(bytes);
out
}
const fn u256_bytes(v: U256) -> [u8; 32] {
v.to_be_bytes()
}
fn u256_be(v: u64) -> [u8; 32] {
let mut out = [0u8; 32];
out[24..].copy_from_slice(&v.to_be_bytes());
out
}
const fn bool_word(v: bool) -> [u8; 32] {
let mut out = [0u8; 32];
out[31] = if v { 1 } else { 0 };
out
}
fn deterministic_salt(d: &GatData) -> B256 {
let o = &d.order;
let mut buf = Vec::with_capacity(20 + 20 + 32 + 4 + 4);
buf.extend_from_slice(o.sell_token.as_slice());
buf.extend_from_slice(o.buy_token.as_slice());
buf.extend_from_slice(&u256_bytes(o.sell_amount));
buf.extend_from_slice(&d.start_time.to_be_bytes());
buf.extend_from_slice(&d.tx_deadline.to_be_bytes());
keccak256(&buf)
}
#[cfg(test)]
mod tests {
use super::*;
fn make_order() -> GpV2OrderStruct {
GpV2OrderStruct {
sell_token: Address::repeat_byte(0x01),
buy_token: Address::repeat_byte(0x02),
receiver: Address::ZERO,
sell_amount: U256::from(1_000u64),
buy_amount: U256::from(900u64),
valid_to: 9_999_999,
app_data: B256::ZERO,
fee_amount: U256::ZERO,
kind: B256::ZERO,
partially_fillable: false,
sell_token_balance: B256::ZERO,
buy_token_balance: B256::ZERO,
}
}
fn make_data() -> GatData {
GatData { order: make_order(), start_time: 1_000_000, tx_deadline: 2_000_000 }
}
#[test]
fn encode_is_448_bytes() {
let data = make_data();
let encoded = encode_gat_struct(&data);
assert_eq!(encoded.len(), 448);
}
#[test]
fn encode_decode_roundtrip() {
let data = make_data();
let encoded = encode_gat_struct(&data);
let decoded = decode_gat_static_input(&encoded).unwrap();
assert_eq!(decoded.start_time, data.start_time);
assert_eq!(decoded.tx_deadline, data.tx_deadline);
assert_eq!(decoded.order.sell_token, data.order.sell_token);
assert_eq!(decoded.order.buy_token, data.order.buy_token);
assert_eq!(decoded.order.sell_amount, data.order.sell_amount);
assert_eq!(decoded.order.buy_amount, data.order.buy_amount);
assert_eq!(decoded.order.valid_to, data.order.valid_to);
assert_eq!(decoded.order.partially_fillable, data.order.partially_fillable);
}
#[test]
fn is_valid_returns_true_for_valid_order() {
let order = GatOrder::new(make_data());
assert!(order.is_valid());
}
#[test]
fn is_valid_returns_false_for_same_tokens() {
let mut data = make_data();
data.order.buy_token = data.order.sell_token;
let order = GatOrder::new(data);
assert!(!order.is_valid());
}
#[test]
fn is_valid_returns_false_when_deadline_before_start() {
let mut data = make_data();
data.tx_deadline = data.start_time - 1;
let order = GatOrder::new(data);
assert!(!order.is_valid());
}
#[test]
fn to_params_sets_correct_handler() {
let order = GatOrder::new(make_data());
let params = order.to_params().unwrap();
assert_eq!(params.handler, GAT_HANDLER_ADDRESS);
}
#[test]
fn decode_too_short_returns_error() {
let result = decode_gat_static_input(&[0u8; 100]);
assert!(result.is_err());
}
}