use nocturne::*;
const TAKE_SELECTOR_HEX: &str = "6a14c9ef";
const CANCEL_ROOT_SELECTOR_HEX: &str = "bb1f12aa";
const RATIFIER_DATA_HEX: &str = "\
000000000000000000000000000000000000000000000000000000000000001c\
1111111111111111111111111111111111111111111111111111111111111111\
2222222222222222222222222222222222222222222222222222222222222222\
3333333333333333333333333333333333333333333333333333333333333333\
0000000000000000000000000000000000000000000000000000000000000002\
00000000000000000000000000000000000000000000000000000000000000c0\
0000000000000000000000000000000000000000000000000000000000000002\
4444444444444444444444444444444444444444444444444444444444444444\
5555555555555555555555555555555555555555555555555555555555555555";
const TAKE_CALLDATA_HEX: &str = "\
6a14c9ef\
00000000000000000000000000000000000000000000000000000000000000e0\
0000000000000000000000000000000000000000000000000000000000000520\
000000000000000000000000000000000000000000000000000000000003d090\
000000000000000000000000cccccccccccccccccccccccccccccccccccccccc\
000000000000000000000000dddddddddddddddddddddddddddddddddddddddd\
000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\
0000000000000000000000000000000000000000000000000000000000000660\
00000000000000000000000000000000000000000000000000000000000001e0\
0000000000000000000000000000000000000000000000000000000000000001\
0000000000000000000000001234567890123456789012345678901234567890\
0000000000000000000000000000000000000000000000000000000000000001\
0000000000000000000000000000000000000000000000000000000077359400\
000000000000000000000000000000000000000000000000000000000000002a\
0000000000000000000000000000000000000000000000000000000000000007\
0000000000000000000000009999999999999999999999999999999999999999\
0000000000000000000000000000000000000000000000000000000000000400\
000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\
0000000000000000000000000000000000000000000000000000000000000001\
00000000000000000000000000000000000000000000000000000000000f4240\
000000000000000000000000000000000000000000000000000000000007a120\
000000000000000000000000000000000000000000000000000000000001e240\
0000000000000000000000000000000000000000000000000000000000000001\
0000000000000000000000001111111111111111111111111111111111111111\
0000000000000000000000002222222222222222222222222222222222222222\
0000000000000000000000000000000000000000000000000000000000000100\
000000000000000000000000000000000000000000000000000000006b49d200\
00000000000000000000000000000000000000000000000000000000000003e8\
0000000000000000000000007777777777777777777777777777777777777777\
0000000000000000000000008888888888888888888888888888888888888888\
0000000000000000000000000000000000000000000000000000000000000002\
0000000000000000000000003333333333333333333333333333333333333333\
0000000000000000000000000000000000000000000000000bef55718ad60000\
0000000000000000000000000000000000000000000000000000000000000001\
0000000000000000000000004444444444444444444444444444444444444444\
0000000000000000000000005555555555555555555555555555555555555555\
0000000000000000000000000000000000000000000000000cb2bba6f17b8000\
0000000000000000000000000000000000000000000000000000000000000002\
0000000000000000000000006666666666666666666666666666666666666666\
0000000000000000000000000000000000000000000000000000000000000004\
deadbeef00000000000000000000000000000000000000000000000000000000\
0000000000000000000000000000000000000000000000000000000000000120\
000000000000000000000000000000000000000000000000000000000000001c\
1111111111111111111111111111111111111111111111111111111111111111\
2222222222222222222222222222222222222222222222222222222222222222\
3333333333333333333333333333333333333333333333333333333333333333\
0000000000000000000000000000000000000000000000000000000000000002\
00000000000000000000000000000000000000000000000000000000000000c0\
0000000000000000000000000000000000000000000000000000000000000002\
4444444444444444444444444444444444444444444444444444444444444444\
5555555555555555555555555555555555555555555555555555555555555555\
0000000000000000000000000000000000000000000000000000000000000002\
cafe000000000000000000000000000000000000000000000000000000000000";
const CANCEL_ROOT_CALLDATA_HEX: &str = "\
bb1f12aa\
0000000000000000000000001234567890123456789012345678901234567890\
3333333333333333333333333333333333333333333333333333333333333333";
fn hx(s: &str) -> Vec<u8> {
assert!(s.len() % 2 == 0, "odd-length hex");
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).expect("valid hex"))
.collect()
}
fn word32(byte: u8) -> Word {
[byte; 32]
}
fn addr(byte: u8) -> Address {
[byte; 20]
}
fn fixture_offer() -> Offer {
let market = Market {
chain_id: word_from_u64(1),
midnight: addr(0x11),
loan_token: addr(0x22),
collateral_params: vec![
CollateralParams {
token: addr(0x33),
lltv: word_from_u128(860_000_000_000_000_000),
liquidation_cursor: word_from_u64(1),
oracle: addr(0x44),
},
CollateralParams {
token: addr(0x55),
lltv: word_from_u128(915_000_000_000_000_000),
liquidation_cursor: word_from_u64(2),
oracle: addr(0x66),
},
],
maturity: word_from_u64(1_800_000_000),
rcf_threshold: word_from_u64(1000),
enter_gate: addr(0x77),
liquidator_gate: addr(0x88),
};
Offer {
market,
buy: true,
maker: [0x12, 0x34, 0x56, 0x78, 0x90].repeat(4).try_into().unwrap(),
start: word_from_u64(1),
expiry: word_from_u64(2_000_000_000),
tick: word_from_u64(42),
group: word_from_u64(7),
callback: addr(0x99),
callback_data: vec![0xde, 0xad, 0xbe, 0xef],
receiver_if_maker_is_seller: addr(0xaa),
ratifier: addr(0xbb),
reduce_only: true,
max_units: 1_000_000,
max_assets: 500_000,
continuous_fee_cap: word_from_u64(123_456),
}
}
fn fixture_sig() -> Sig {
Sig {
r: word32(0x11),
s: word32(0x22),
v: 28,
}
}
#[test]
fn selectors_match_solidity() {
assert_eq!(TAKE_SELECTOR.to_vec(), hx(TAKE_SELECTOR_HEX));
assert_eq!(CANCEL_ROOT_SELECTOR.to_vec(), hx(CANCEL_ROOT_SELECTOR_HEX));
}
#[test]
fn ratifier_data_matches_solidity() {
let sig = fixture_sig();
let root = word32(0x33);
let proof = [word32(0x44), word32(0x55)];
let got = encode_ratifier_data(&sig, &root, 2, &proof);
assert_eq!(got, hx(RATIFIER_DATA_HEX));
}
#[test]
fn take_calldata_matches_solidity() {
let offer = fixture_offer();
let sig = fixture_sig();
let root = word32(0x33);
let proof = [word32(0x44), word32(0x55)];
let ratifier_data = encode_ratifier_data(&sig, &root, 2, &proof);
let got = encode_take_calldata(
&offer,
&ratifier_data,
U256::from(250_000u64),
&addr(0xcc),
&addr(0xdd),
&addr(0xee),
&[0xca, 0xfe],
);
assert_eq!(got, hx(TAKE_CALLDATA_HEX));
assert_eq!(&got[..4], &TAKE_SELECTOR);
assert_eq!((got.len() - 4) % 32, 0);
}
#[test]
fn cancel_root_calldata_matches_solidity() {
let maker: Address = [0x12, 0x34, 0x56, 0x78, 0x90].repeat(4).try_into().unwrap();
let root = word32(0x33);
let got = encode_cancel_root_calldata(&maker, &root);
assert_eq!(got, hx(CANCEL_ROOT_CALLDATA_HEX));
assert_eq!(&got[..4], &CANCEL_ROOT_SELECTOR);
assert_eq!(got.len(), 4 + 64); }
#[test]
fn decode_take_calldata_matches_solidity() {
let decoded = decode_take_calldata(&hx(TAKE_CALLDATA_HEX)).unwrap();
assert_eq!(decoded.offer, fixture_offer());
assert_eq!(decoded.units, U256::from(250_000u64));
assert_eq!(decoded.taker, addr(0xcc));
assert_eq!(decoded.receiver_if_taker_is_seller, addr(0xdd));
assert_eq!(decoded.taker_callback, addr(0xee));
assert_eq!(decoded.taker_callback_data, vec![0xca, 0xfe]);
let RatifierPayload::Ecrecover(rd) = &decoded.ratifier_data else {
panic!("expected ecrecover ratifier data");
};
assert_eq!(rd.sig, fixture_sig());
assert_eq!(rd.root, word32(0x33));
assert_eq!(rd.leaf_index, 2);
assert_eq!(rd.proof, vec![word32(0x44), word32(0x55)]);
}
#[test]
fn decode_ratifier_data_matches_solidity() {
let rd = decode_ratifier_data(&hx(RATIFIER_DATA_HEX)).unwrap();
assert_eq!(rd.sig, fixture_sig());
assert_eq!(rd.root, word32(0x33));
assert_eq!(rd.leaf_index, 2);
assert_eq!(rd.proof, vec![word32(0x44), word32(0x55)]);
}
#[test]
fn decode_cancel_root_calldata_matches_solidity() {
let (maker, root) = decode_cancel_root_calldata(&hx(CANCEL_ROOT_CALLDATA_HEX)).unwrap();
let expected: Address = [0x12, 0x34, 0x56, 0x78, 0x90].repeat(4).try_into().unwrap();
assert_eq!(maker, expected);
assert_eq!(root, word32(0x33));
}
#[test]
fn empty_proof_and_bytes_encode_cleanly() {
let sig = Sig {
r: word32(1),
s: word32(2),
v: 27,
};
let rd = encode_ratifier_data(&sig, &word32(9), 0, &[]);
assert_eq!(rd.len(), 7 * 32);
assert_eq!(word_to_u128(&rd[160..192].try_into().unwrap()), Some(192));
assert_eq!(rd[192..224], [0u8; 32]);
}
#[test]
fn take_calldata_embeds_ratifier_data_verbatim() {
let offer = fixture_offer();
let ratifier_data = encode_ratifier_data(
&fixture_sig(),
&word32(0x33),
2,
&[word32(0x44), word32(0x55)],
);
let calldata = encode_take_calldata(
&offer,
&ratifier_data,
U256::from(1u64),
&addr(0xcc),
&addr(0xdd),
&addr(0xee),
&[],
);
let window = ratifier_data.len();
assert!(
calldata
.windows(window)
.any(|w| w == ratifier_data.as_slice()),
"ratifierData should be embedded verbatim in the take calldata"
);
}